home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume38 / phonewrd / part01 < prev    next >
Encoding:
Text File  |  1993-07-13  |  61.5 KB  |  2,222 lines

  1. Newsgroups: comp.sources.misc
  2. From: erich@eye.com (Eric Haines)
  3. Subject: v38i056:  phonewrd - phone number phrase generator, Part01/01
  4. Message-ID: <1993Jul14.143750.25435@sparky.sterling.com>
  5. X-Md4-Signature: c6f43784aa974c49f3a45f0975d6a273
  6. Sender: kent@sparky.sterling.com (Kent Landfield)
  7. Organization: Sterling Software
  8. Date: Wed, 14 Jul 1993 14:37:50 GMT
  9. Approved: kent@sparky.sterling.com
  10.  
  11. Submitted-by: erich@eye.com (Eric Haines)
  12. Posting-number: Volume 38, Issue 56
  13. Archive-name: phonewrd/part01
  14. Environment: UNIX, DOS
  15.  
  16. This is the phonewrd program, which figures out phrases that fit your phone
  17. number (you know, 443-2788 spells "I heart U", 477-2338 spells "I spade U").
  18. In the time-honored tradition of UNIX, it has a gob of command line options, a
  19. few of which are even useful.  It normally uses /usr/dict/words, but if you
  20. don't have a plaintext dictionary around it can generate tables for easy
  21. searching or, better yet, you can get GNU's free ispell dictionaries (see the
  22. man page for details) and use these.
  23.  
  24. This beastie should work under UNIX and DOS (and whatever else) under all the
  25. flavors of C.  Now that I've said that I've doomed myself to some weird
  26. portability problem.  You'll want to define USE_STRINGS_H in the makefile if
  27. you use <strings.h> on your system.  Also, you'll almost definitely want to
  28. change DICT_PATH in the code to wherever your dictionary resides.
  29.  
  30. Bugs, etc to:
  31. Eric Haines, erich@eye.com
  32.  
  33. #---------------------------------- cut here ----------------------------------
  34. #! /bin/sh
  35. # This is a shell archive.  Remove anything before this line, then feed it
  36. # into a shell via "sh file" or similar.  To overwrite existing files,
  37. # type "sh file -c".
  38. # Contents:  README makefile patchlevel.h phonewrd.1 phonewrd.c
  39. #   phonewrd.txt
  40. # Wrapped by kent@sparky on Fri Jul  9 16:48:54 1993
  41. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin ; export PATH
  42. echo If this archive is complete, you will see the following message:
  43. echo '          "shar: End of archive 1 (of 1)."'
  44. if test -f 'README' -a "${1}" != "-c" ; then 
  45.   echo shar: Will not clobber existing file \"'README'\"
  46. else
  47.   echo shar: Extracting \"'README'\" \(1230 characters\)
  48.   sed "s/^X//" >'README' <<'END_OF_FILE'
  49. XThis is the phonewrd program, which figures out phrases that fit your phone
  50. Xnumber (you know, 443-2788 spells "I heart U", 477-2338 spells "I spade U").
  51. XIn the time-honored tradition of UNIX, it has a gob of command line options, a
  52. Xfew of which are even useful.  It normally uses /usr/dict/words, but if you
  53. Xdon't have a plaintext dictionary around it can generate tables for easy
  54. Xsearching or, better yet, you can get GNU's free ispell dictionaries (see the
  55. Xman page for details) and use these.
  56. X
  57. XThis beastie should work under UNIX and DOS (and whatever else) under all the
  58. Xflavors of C.  Now that I've said that I've doomed myself to some weird
  59. Xportability problem.  You'll want to define USE_STRINGS_H in the makefile if
  60. Xyou use <strings.h> on your system.  Also, you'll almost definitely want to
  61. Xchange DICT_PATH in the code to wherever your dictionary resides.
  62. X
  63. XFiles included in this distribution:
  64. X    README - what the heck could be in here?
  65. X    makefile - unix makefile (can you say trivial? edit it yourself)
  66. X    phonewrd.c - the code (which became spaghetti-like over time...)
  67. X    phonewrd.1 - the man page
  68. X    phonewrd.txt - the man page in plaintext
  69. X    patchlevel.h - version and patch values
  70. X
  71. XBugs, etc to:
  72. XEric Haines, erich@eye.com
  73. END_OF_FILE
  74.   if test 1230 -ne `wc -c <'README'`; then
  75.     echo shar: \"'README'\" unpacked with wrong size!
  76.   fi
  77.   # end of 'README'
  78. fi
  79. if test -f 'makefile' -a "${1}" != "-c" ; then 
  80.   echo shar: Will not clobber existing file \"'makefile'\"
  81. else
  82.   echo shar: Extracting \"'makefile'\" \(322 characters\)
  83.   sed "s/^X//" >'makefile' <<'END_OF_FILE'
  84. X# trivial makefile - you edit it...
  85. X
  86. XCC_OPTS=-O
  87. X# define USE_STRINGS_H if you have <strings.h> instead of <string.h>
  88. X# CC_OPTS=-O -DUSE_STRINGS_H
  89. X
  90. Xphonewrd:    phonewrd.c patchlevel.h
  91. X        cc -o phonewrd phonewrd.c $(CC_OPTS)
  92. X
  93. Xinstall:    phonewrd
  94. X        cp phonewrd /usr/contrib/bin
  95. X        cp phonewrd.1 /usr/man/man1
  96. X
  97. Xclean:
  98. X        rm phonewrd
  99. END_OF_FILE
  100.   if test 322 -ne `wc -c <'makefile'`; then
  101.     echo shar: \"'makefile'\" unpacked with wrong size!
  102.   fi
  103.   # end of 'makefile'
  104. fi
  105. if test -f 'patchlevel.h' -a "${1}" != "-c" ; then 
  106.   echo shar: Will not clobber existing file \"'patchlevel.h'\"
  107. else
  108.   echo shar: Extracting \"'patchlevel.h'\" \(52 characters\)
  109.   sed "s/^X//" >'patchlevel.h' <<'END_OF_FILE'
  110. X#define VERSION        "version 2.0"
  111. X#define PATCHLEVEL    0
  112. END_OF_FILE
  113.   if test 52 -ne `wc -c <'patchlevel.h'`; then
  114.     echo shar: \"'patchlevel.h'\" unpacked with wrong size!
  115.   fi
  116.   # end of 'patchlevel.h'
  117. fi
  118. if test -f 'phonewrd.1' -a "${1}" != "-c" ; then 
  119.   echo shar: Will not clobber existing file \"'phonewrd.1'\"
  120. else
  121.   echo shar: Extracting \"'phonewrd.1'\" \(9688 characters\)
  122.   sed "s/^X//" >'phonewrd.1' <<'END_OF_FILE'
  123. X.\" -*-Text-*-
  124. X.\";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  125. X.\"
  126. X.\" File:         phonewrd.1
  127. X.\" Description:  phonewrd man page
  128. X.\" Author:       Eric Haines, erich@eye.com
  129. X.\" Modified:     4/21/93
  130. X.\" Package:      phonewrd
  131. X.\"
  132. X.\" (c) Copyright 1993, Eric Haines, all rights reserved.
  133. X.\"
  134. X.\";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  135. X.TH PHONEWRD 1 "" ""
  136. X.ad l
  137. X.SH NAME
  138. Xphonewrd \- find words or phrases for phone numbers
  139. X
  140. X.SH SYNOPSIS
  141. X
  142. X.BR phonewrd
  143. X[\fB-n \fInumerals allowed\fR]
  144. X[\fB-s \fIsingle letter words\fR]
  145. X[\fB-q \fIq mapping\fR]
  146. X[\fB-z \fIz mapping\fR]
  147. X[\fB-d \fIdictionary path\fR]
  148. X[\fB-l \fIword length\fR]
  149. X[\fB-m \fInumeral map\fR]
  150. X[\fB-v\fR]
  151. X[\fB-r\fR]
  152. X[\fB-c\fR]
  153. X[\fB-C\fR]
  154. X\fBphone_number1[*...]\fR [\fBphone_number2[*...] ...\fR]
  155. X
  156. X.SH DESCRIPTION
  157. X
  158. X.B phonewrd
  159. Xfinds words or phrases which match the input phone numbers.
  160. XIt uses the dictionary word list
  161. X.I /usr/dict/words
  162. Xby default, ignoring one letter words (see \fB-s\fR).
  163. XThere are a wide variety of options available to allow various sorts of
  164. Xpattern matching.
  165. XWords with apostrophes or other punctuation are acceptable.
  166. XIf more than one phone number is given, each number and its combinations are
  167. Xoutput.
  168. X
  169. XIf the input phone number ends with one or more \fI*\fR, e.g. \fI555-1212**\fR,
  170. Xthen the output phrases can be up to that many characters longer that the input
  171. Xnumber.
  172. XThis usually yields a few extra phrases, and
  173. Xdialing those extra digits at the end normally doesn't cause problems.
  174. X
  175. XThe input phone number can have letters in it, in which case that digit is
  176. Xforced to be that letter in any phrase.
  177. XThis can help pare down a long output list for a number.
  178. X
  179. XIf the number is entirely letters, it is converted to numbers and output.
  180. X
  181. XIf you don't have a dictionary on your system, a free set of dictionary files
  182. Xare available via FTP from prep.ai.mit.edu in /pub/gnu/ispell*.tar.z (you'll
  183. Xalso need "gzip" in this same directory to uncompress this archive).
  184. X.B phonewrd
  185. Xunderstands the "affix" format used for the
  186. Xdictionaries in ispell and expands words accordingly.
  187. X
  188. XIf you can't get your hands on any dictionary files,
  189. Xlook at the \fB-r\fR, \fB-c\fR, and (for the totally insane) \fB-C\fR options.
  190. XThese options do not need a dictionary (and so will do either very little or
  191. Xtoo much).
  192. X
  193. X.SH Options
  194. X.ifn .TP 5
  195. X.ift .TP 15
  196. X.B -n \fInumerals allowed\fP
  197. XThis option allows phone digits to be replaced by numerals, up to the limit
  198. Xset.
  199. XFor example
  200. X.B -n 2
  201. Xmeans that any two digits of the phone number can be left as digits and not
  202. Xmade a part of any word.
  203. XThis can help find phrases for phone numbers with poor letter combinations.
  204. XNote that numerals that have no corresponding letters
  205. X(i.e. 0 and 1 by default) do not count against this total, nor do one letter
  206. Xwords set using \fB-s\fR.
  207. XDefault setting is 0.
  208. X
  209. X.TP
  210. X.B -s \fIsingle letter words\fP
  211. XThis option gives all allowed single letter words (including numeral "words",
  212. Xe.g. "I love U 2").
  213. XNormally dictionaries have all letters of the alphabet as being legal words,
  214. Xwhich gives a ton of useless phrases.
  215. XBy default the only allowed single letter words are "a", "i", and "o".
  216. XAs an example of how to change this,
  217. X.B -s abcdgimoptuxy248
  218. Xmeans that any of these single letters are now considered words.
  219. XThis can help find phrases for phone numbers with awful
  220. Xletter combinations and in general open up more possibilities.
  221. XTo have no single letter words at all, use \fB-s =\fR.
  222. X
  223. X.TP
  224. X.B -q \fImapping\fP
  225. XThis option maps the letter
  226. X.I q
  227. Xto the given numeral.
  228. XFor example,
  229. X.B -q 0
  230. Xmeans that phone numbers with 0's in them can have \fIq\fR's
  231. Xsubstituted for the 0's.
  232. XThis usually doesn't help much anyway, and it's up to you to remember that
  233. X.I q
  234. X== 0, but there you go.
  235. XBy default
  236. X.I q
  237. Xis not mapped.
  238. X
  239. X.TP
  240. X.B -z \fImapping\fP
  241. XAmazingly similar to \fB-q\fR.
  242. XThis option maps the letter
  243. X.I z
  244. Xto the given numeral.
  245. XFor example,
  246. X.B -z 0
  247. Xmeans that phone numbers with 0's in them can have \fIz\fR's
  248. Xsubstituted for the 0's.
  249. XThis usually doesn't help much anyway, and it's up to you to remember that
  250. X.I z
  251. X== 0, but there it is.
  252. XBy default
  253. X.I z
  254. Xis not mapped.
  255. X
  256. X.TP
  257. X.B -d \fIdictionary path\fP
  258. XThis option allows alternate dictionaries to be used.
  259. XFor example,
  260. X.in +0.5i
  261. X.B -d /users/fred/dict/star_trek_words
  262. X.in -0.5i
  263. Xwill use (only) Fred's geek-speak dictionary.
  264. XDefault is \fI/usr/dict/words\fR (or whatever it is on your system),
  265. Xwhich is not used if any dictionary path is specified.
  266. X
  267. XMultiple dictionaries can be used by specifying multiple paths.  For example,
  268. Xto use the geek speak dictionary along with the regular system dictionary, do:
  269. X.in +0.5i
  270. X.B -d /users/fred/dict/star_trek_words -d /usr/dict/words
  271. X.in -0.5i
  272. XA path of "." means use the default dictionary, so quicker yet is:
  273. X.in +0.5i
  274. X.B -d . -d /users/fred/dict/star_trek_words
  275. X.in -0.5i
  276. XIn this way additional
  277. Xdictionaries can either replace or be added to the default.
  278. X
  279. XNote that
  280. X.B phonewrd
  281. Xunderstands the "affix" format used for the dictionaries of GNU's ispell and
  282. Xexpands words accordingly, so these can be used to add more words to the
  283. Xsearch.
  284. X
  285. X.TP
  286. X.B -l \fIword length\fP
  287. XThis option allows a minimum word length to be set.
  288. XFor example,
  289. X.B -l 3
  290. Xmeans no words shorter than 3 letters are used for building phrases.
  291. XUser defined single letter words (see \fB-s\fR) are not affected by this
  292. Xoption.
  293. XSingle letter words from the dictionary are ignored no matter
  294. Xwhat the setting (since most dictionaries include exciting words like "h",
  295. X"w" and so on).
  296. XDefault is 1.
  297. X
  298. X.TP
  299. X.B -m \fInumeral map\fP
  300. XFor those of you from a different planet, this will remap the letters to
  301. Xdifferent numerals.
  302. XFor example,
  303. X.in +0.5i
  304. X.B -m 2223334445556667.77888999.
  305. X.in -0.5i
  306. Xis the default setting, a normal telephone.
  307. XThe "\fB.\fR" (or any other non-numeric value) means don't map the letter to
  308. Xany key.
  309. XYou get a banana for using \fB-v\fR with this command.
  310. X
  311. X.TP
  312. X.B -v
  313. XVerbose option:  words fitting into the various number slots are output
  314. Xand other warnings and whatnot are all output to stderr.
  315. XThis can help you "roll your own" if the program does not find anything to
  316. Xyour liking.
  317. X
  318. X.TP
  319. X.B -r
  320. XThis option is for those without a dictionary.
  321. XIt simply outputs the letters
  322. Xpossible for each digit in a column so that you can search for combinations
  323. Xmore easily.
  324. XNo dictionary search is done, so none is needed.
  325. X
  326. X.TP
  327. X.B -c
  328. XThis option is also for those without a dictionary.
  329. XIt generates all
  330. Xpermutations of letters and numerals valid for the input phone number and
  331. Xoutputs these one per line.
  332. XIf you do:
  333. X
  334. X    phonewrd -c 234-5678 > junk1
  335. X.br
  336. X    spell junk1 > junk2
  337. X.br
  338. X    diff junk1 junk2 | grep "<"
  339. X
  340. Xyou'll get any 7 letter words formed which pass the spell checker.
  341. XIt's a long shot, but you never know...
  342. X
  343. X.TP
  344. X.B -C
  345. XThis option is also for those without a dictionary and too much disk space and
  346. XCPU time.
  347. XIt generates all
  348. Xpermutations of letters and numerals valid for the input phone number and
  349. Xoutputs these one per line, plus it makes all permutations of spacing for
  350. Xeach of these permutations.
  351. X3^7 * 2^6 = 139968 phrases for a phone number without 0's and 1's.
  352. XThen send each one through your spell checker and if any errors are found,
  353. Xdiscard it and go to the next.
  354. XIt will take awhile, but this is the price you pay for not having any
  355. Xdictionary available.
  356. X
  357. X.SH EXAMPLES
  358. X.ne 4
  359. X.B phonewrd 542-5968
  360. Xgives the words:
  361. X.br
  362. X    kick you
  363. X.br
  364. X    lick you
  365. X
  366. X.ne 7
  367. X.B phonewrd 542-5968 -n 1
  368. X.br
  369. Xallows more slack, letting one character be a numeral:
  370. X.br
  371. X    kick you
  372. X.br
  373. X    lick you
  374. X.br
  375. X    5 gal you
  376. X.br
  377. X    5 Hal you
  378. X.br
  379. Xwhich gives a little wider range of possibilities.
  380. X
  381. X.ne 5
  382. X.B phonewrd 5683968 -l 3
  383. X.br
  384. Xgives words 3 characters or longer:
  385. X.br
  386. X    Jove you
  387. X.br
  388. X    loud you
  389. X.br
  390. X    love you
  391. X
  392. X.B phonewrd 486-9364 -s aio248
  393. X.br
  394. Xgives many combinations, but the
  395. X.B -s aio248
  396. Xallows the extra combination
  397. X.br
  398. X    I 8 my dog
  399. X.br
  400. X
  401. X.B phonewrd 787-3648*
  402. X.br
  403. Xallows the extra combination
  404. X.br
  405. X    strength
  406. X.br
  407. Xas one \fI*\fR at the end means up to 1 additional character can be output
  408. Xat the end of the phrase.
  409. X
  410. X.B phonewrd 861-2076
  411. X.br
  412. Xdoesn't yield much of anything, so setting \fI0\fR to \fIo\fR and
  413. X\fI1\fR to \fIi\fR:
  414. X.br
  415. X.B phonewrd 86i-2o76
  416. X.br
  417. Xwe get
  418. X.br
  419. X    unicorn
  420. X.br
  421. Xso \fIun1c0rn\fR fits the original phone number.
  422. XPutting a letter in a phone number
  423. Xforces the use of that letter in that position.
  424. X
  425. X.B phonewrd -r 266-7883
  426. X.br
  427. Xyields
  428. X.ne 3
  429. X.br
  430. X      a  m  m  p  t  t  d
  431. X.br
  432. X      b  n  n  r  u  u  e
  433. X.br
  434. X      c  o  o  s  v  v  f
  435. X.br
  436. XThe letters for each numeral are output in columns and so can be more easily
  437. Xsearched by hand for interesting phrases.
  438. XYes, I know you found \fIbon stud\fR
  439. Xin there, but what seven letter word can be found?
  440. XIf you find it, pat
  441. Xyourself on the cranium for having a brain and being able to quickly cull
  442. Xthrough the 2187 possibilities.
  443. X
  444. X.B phonewrd -c 2667883
  445. X.br
  446. Xwill list all the possible combinations, e.g.
  447. X.br
  448. X    ammpttd
  449. X.br
  450. X    ammptte
  451. X.br
  452. X    ammpttf
  453. X.br
  454. X    ammptud
  455. X.br
  456. X    ammptue
  457. X.br
  458. X    ...
  459. X.br
  460. Xwhich can then be run through a spell checker and so searched for seven
  461. Xletter words (see the \fB-c\fR option for how to do this).
  462. X
  463. X.B phonewrd 234-5678 -v
  464. X.br
  465. Xwill output all words which fit into the various places in the phone number.
  466. XFor example, one output entry is:
  467. X.ne 2
  468. X.br
  469. X    Digit 5, length 3:
  470. X.br
  471. X     opt
  472. X.br
  473. Xwhich means that for the fifth digit of the number, all words of length three
  474. Xwhich fit (i.e. translate into "678" in this case) are displayed.
  475. X
  476. X.SH TO BE DONE
  477. XIt would be cute to be able to map numerals to strings, e.g. 460-2253 with
  478. X"0" mapped to "OPER" gives "inOPERable", or 434-4637 with "4" mapped to "for"
  479. Xgives "FOReigner".
  480. X
  481. X.SH AUTHOR
  482. XEric Haines, erich@eye.com
  483. END_OF_FILE
  484.   if test 9688 -ne `wc -c <'phonewrd.1'`; then
  485.     echo shar: \"'phonewrd.1'\" unpacked with wrong size!
  486.   fi
  487.   # end of 'phonewrd.1'
  488. fi
  489. if test -f 'phonewrd.c' -a "${1}" != "-c" ; then 
  490.   echo shar: Will not clobber existing file \"'phonewrd.c'\"
  491. else
  492.   echo shar: Extracting \"'phonewrd.c'\" \(35263 characters\)
  493.   sed "s/^X//" >'phonewrd.c' <<'END_OF_FILE'
  494. X/*
  495. X   Phonewrd - given a phone number, find all words or phrases in a dictionary
  496. X    which fit it.  See the man page for details, type "phonewrd -?" for
  497. X    options.
  498. X
  499. X   version 2.0 - 6/23/93
  500. X
  501. X   (c) copyright 1993, Eric Haines, all rights reserved (erich@eye.com)
  502. X
  503. X
  504. X   History:
  505. X
  506. X   2.0 - release for comp.sources.misc
  507. X */
  508. X
  509. X#include <stdlib.h>    /* if you don't have stdlib, use malloc.h instead */
  510. X#include <stdio.h>
  511. X
  512. X/* define USE_STRINGS_H if you use <strings.h> instead of <string.h> */
  513. X#ifdef USE_STRINGS_H
  514. X    /* BSD string routines. */
  515. X#   include <strings.h>
  516. X/* Really, should not define USE_STRINGS_H if __STDC__, but be safe. */
  517. X#ifndef __STDC__
  518. X#   define strchr index
  519. X#   define strrchr rindex
  520. X#endif /* not __STDC__ */
  521. X#else
  522. X    /* SYS V string routines. */
  523. X#   include <string.h>
  524. X#endif /* USE_STRINGS_H */
  525. X
  526. X#include "patchlevel.h"
  527. X
  528. X/*============= default related =========================================== */
  529. X
  530. X/* path to dictionary */
  531. X#define    DICT_PATH    "/usr/dict/words"
  532. X
  533. X/* number of digits in phone number */
  534. X#define    NUM_DIGITS    7
  535. X
  536. X/* how many numerals are allowed in our phrase? */
  537. X#define    NUMERALS_ALLOWED    0
  538. X
  539. X/* minimum length of words: note that 1 always gets bumped to 2, because of
  540. X * OneLetter below.
  541. X */
  542. X#define    MIN_LENGTH    1
  543. X
  544. X/* one letter words that are acceptable */
  545. X#define    ONE_LETTER    "aio"
  546. X/* If you like things like "CDB" (see the bee) or "IM4U, use or modify the
  547. X * array below.  Yes, "j" and "k" are potential names, "m" is only in "I am",
  548. X * "q" is queue, etc - you decide...  This option tends to generate lots of
  549. X * useless goop, but it's good if you're desperate.
  550. X */
  551. X/* #define ONE_LETTER    "abcdgimoptuxy248" */
  552. X
  553. X#define    NO_NUMBER    -2
  554. X
  555. X/* to make Q and Z not be anything, set them to NO_NUMBER, else set them
  556. X * to the digit you want (e.g. 0 or 1)
  557. X */
  558. X#define    Q    NO_NUMBER
  559. X#define    Z    NO_NUMBER
  560. X
  561. X/* translation table for letters to numbers */
  562. Xint    Letter2Numeral[] =
  563. X    /* a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z */
  564. X     { 2,2,2,3,3,3,4,4,4,5,5,5,6,6,6,7,Q,7,7,8,8,8,9,9,9,Z } ;
  565. X
  566. X/* true if a character is a vowel */
  567. X#define    Vowel(c)    strchr( "aeiou", (c) )
  568. X
  569. X/*============= storage space related ===================================== */
  570. X
  571. X#define MAX_DIGITS    14
  572. X#define    MAX_INDICES    (((MAX_DIGITS+1)*MAX_DIGITS)/2)
  573. X
  574. X/* increment for word list memory space */
  575. X#define    WORD_LIST_SIZE    50
  576. X
  577. X/*============= internal use constants ==================================== */
  578. X
  579. X#define    FIXED_LETTER    -1
  580. X
  581. X#define    BUFSIZE        256
  582. X#ifndef    TRUE
  583. X#define    TRUE    1
  584. X#define    FALSE    0
  585. X#endif
  586. X
  587. X/*============= structures ================================================ */
  588. X
  589. X/* array of words, indexed by digit location and length of word.  This list is
  590. X * built up in the first part of the program, as each dictionary word is
  591. X * categorized and put in its list.  Then this structure is traversed to make
  592. X * up the combinations.
  593. X */
  594. Xtypedef    struct {
  595. X    char    **p_word ;    /* list of pointers to words */
  596. X    int    count ;        /* current count */
  597. X    int    size ;        /* allocated size */
  598. X} wordlist, *p_wordlist ;
  599. X
  600. X/*============= data and macros =========================================== */
  601. X
  602. X#define    IndexWL( digit_loc, length )    ( WLoffset[digit_loc] + length - 1 )
  603. X
  604. Xwordlist WL[MAX_INDICES] ;
  605. Xint    WLoffset[MAX_DIGITS] ;
  606. Xint    PhoneNum[MAX_DIGITS+1] ;    /* phone number translation */
  607. Xchar    PhoneStr[MAX_DIGITS+1] ;    /* phone number letter, if specified */
  608. Xint    NumeralMapped[10] ;        /* if TRUE, then index # is mapped */
  609. Xchar    OneLetter[BUFSIZE] ;
  610. Xchar    *DictPath[100] ;
  611. Xint    DictTot ;
  612. Xchar    *ProgName ;
  613. X
  614. Xint    NumDigits ;
  615. Xint    NumIndices ;
  616. Xint    NumNumerals ;
  617. Xint    MinLength ;
  618. Xint    RollOwn ;
  619. Xint    Concat ;
  620. Xint    Verbose ;
  621. Xint    MatchTot ;
  622. Xint    Wildcard ;
  623. Xint    TotBrk ;
  624. X
  625. Xint    HoldSize ;
  626. Xint    HoldCount ;
  627. Xchar    **HoldWord ;
  628. X
  629. Xchar    OutputGrid[MAX_DIGITS][27] ;
  630. X
  631. X/*============= procedure declarations ==================================== */
  632. X
  633. Xint phone_check() ;
  634. Xvoid roll_own() ;
  635. Xvoid concat_it() ;
  636. Xvoid concat_letter_out() ;
  637. Xvoid concat_letter_breaks_out() ;
  638. Xvoid init_wl() ;
  639. Xint fit_word() ;
  640. Xint permute_word() ;
  641. Xvoid search_for_match() ;
  642. Xvoid hold_word() ;
  643. Xint store_word() ;
  644. Xvoid free_wl() ;
  645. Xint scan_options() ;
  646. Xchar *str_duplicate() ;
  647. X
  648. X/*============= procedures ================================================ */
  649. X
  650. Xmain(argc,argv)
  651. Xint argc;  char *argv[];
  652. X{
  653. Xint    nargc ;
  654. Xchar    *nargv[BUFSIZE], *targv ;
  655. Xint    i, hdr_out, cont_flag ;
  656. X
  657. X    ProgName = argv[0] ;
  658. X
  659. X    HoldSize = HoldCount = 0 ;
  660. X
  661. X    NumNumerals = NUMERALS_ALLOWED ;
  662. X    MinLength = MIN_LENGTH ;
  663. X    RollOwn = FALSE ;
  664. X    Concat = 0 ;
  665. X    Verbose = FALSE ;
  666. X
  667. X    strcpy( OneLetter, ONE_LETTER ) ;
  668. X    *DictPath = str_duplicate( DICT_PATH ) ;
  669. X    if ( !(*DictPath) ) {
  670. X    fprintf( stderr, "Ugh, we're out of memory!\n" ) ;
  671. X    exit(1) ;
  672. X    }
  673. X    DictTot = 1 ;
  674. X
  675. X    /* translate phone number */
  676. X    if ( argc < 2 ) {
  677. X    fprintf( stderr, "Not enough arguments.\n" ) ;
  678. X    usage() ;
  679. X    return( 1 ) ;
  680. X    }
  681. X
  682. X    cont_flag = scan_options( argc, argv, &nargc, nargv ) ;
  683. X    if ( Verbose ) {
  684. X    /* always print out the version, etc, even if errors occur */
  685. X    fprintf( stdout, "%s %s, patch level %d\n",
  686. X        argv[0], VERSION, PATCHLEVEL ) ;
  687. X    }
  688. X
  689. X    if ( !cont_flag ) {
  690. X    /* bad scan option, so quit */
  691. X    return( 1 ) ;
  692. X    }
  693. X
  694. X    if ( nargc < 1 ) {
  695. X    fprintf( stderr, "No phone number found.\n" ) ;
  696. X    usage() ;
  697. X    return( 1 ) ;
  698. X    }
  699. X
  700. X    /* loop through phone number(s), output headers if more than one */
  701. X    hdr_out = ( nargc > 1 ) ;
  702. X    for ( i = 0 ; i < nargc ; i++ ) {
  703. X    if ( i ) {
  704. X        /* output carriage returns before 2nd, 3rd, etc */
  705. X        fprintf( stdout, "\n" ) ;
  706. X    }
  707. X    targv = nargv[i] ;
  708. X    if ( hdr_out ) {
  709. X        fprintf( stdout, "# %s\n", targv ) ;
  710. X    }
  711. X    if ( phone_check( targv ) ) {
  712. X        /* something very bad happened, so quit */
  713. X        return( 1 ) ;
  714. X    }
  715. X    }
  716. X
  717. X    return( 0 ) ;
  718. X}
  719. X
  720. Xusage()
  721. X{
  722. X    fprintf(stderr, "usage: %s [options] phone#[*...]\n", ProgName);
  723. X    fprintf(stderr, " [*...] - extra *'s at the end mean optional wildcard letters\n");
  724. X    fprintf(stderr, "  -l # - minimum length of words (cur. == %d)\n", MinLength);
  725. X    fprintf(stderr, "  -n # - number of numerals allowed in phrase (cur. == %d)\n", NumNumerals);
  726. X    if ( Letter2Numeral['q'-'a'] == NO_NUMBER ) {
  727. X    fprintf(stderr, "  -q # - mapping of q (none currently)\n" ) ;
  728. X    } else {
  729. X    fprintf(stderr, "  -q # - mapping of q (cur. == %d)\n",
  730. X            Letter2Numeral['q'-'a'] ) ;
  731. X    }
  732. X    if ( Letter2Numeral['z'-'a'] == NO_NUMBER ) {
  733. X    fprintf(stderr, "  -z # - mapping of z (none currently)\n" ) ;
  734. X    } else {
  735. X    fprintf(stderr, "  -z # - mapping of z (cur. == %d)\n",
  736. X            Letter2Numeral['z'-'a'] ) ;
  737. X    }
  738. X    fprintf(stderr, "  -d string - dictionary path (cur. == %s)\n", *DictPath);
  739. X    fprintf(stderr, "  -s string - allowed single letter words (cur. == \"%s\")\n", OneLetter);
  740. X    fprintf(stderr, "  -m char[26] - mapping of entire alphabet\n");
  741. X    fprintf(stderr, "  -r - output the corresponding letters in an array (no dictionary)\n");
  742. X    fprintf(stderr, "  -c - output all combinations (no dictionary)\n");
  743. X    fprintf(stderr, "  -C - output all combinations and spacings (no dictionary)\n");
  744. X    fprintf(stderr, "  -v - verbose output (show words that do fit)\n");
  745. X}
  746. X
  747. X
  748. Xint
  749. Xphone_check( numeral_string )
  750. Xchar    *numeral_string ;
  751. X{
  752. Xint    i, tot, word_count, length, min_length, found_digit, val, nn ;
  753. Xchar    tchr, *tstr, **dp ;
  754. Xchar    dict_word[BUFSIZE] ;
  755. Xchar    out_string[BUFSIZE] ;
  756. Xp_wordlist    p_wl ;
  757. XFILE    *infile ;
  758. X
  759. X    NumDigits = 0 ;
  760. X    TotBrk = -1 ;
  761. X
  762. X    tstr = numeral_string ;
  763. X    tchr = *tstr++ ;
  764. X    found_digit = FALSE ;
  765. X    while ( tchr != '\0' && tchr != '*' && (NumDigits <= MAX_DIGITS ) ) {
  766. X    if ( ( tchr >= '0' ) && ( tchr <= '9' ) ) {
  767. X        /* convert to digit */
  768. X        PhoneNum[NumDigits] = (int)(tchr - '0') ;
  769. X        PhoneStr[NumDigits++] = tchr ;
  770. X        found_digit = TRUE ;
  771. X    } else if ( ( (tchr >= 'A') && (tchr <= 'Z') ) ||
  772. X            ( (tchr >= 'a') && (tchr <= 'z') ) ) {
  773. X        PhoneNum[NumDigits] = FIXED_LETTER ;
  774. X        PhoneStr[NumDigits++] = tolower( tchr ) ;
  775. X    }
  776. X    tchr = *tstr++ ;
  777. X    }
  778. X    PhoneStr[NumDigits]='\0' ;
  779. X
  780. X    if ( NumDigits <= 0 ) {
  781. X    fprintf( stderr, "Sorry, no digits input in `%s'\n", numeral_string ) ;
  782. X    fprintf( stderr, "Recompile with a higher MAX_DIGITS setting.\n" ) ;
  783. X    usage() ;
  784. X    return(0) ;
  785. X    }
  786. X
  787. X    /* check if we're in roll own mode */
  788. X    if ( RollOwn ) {
  789. X    roll_own() ;
  790. X    return(0) ;
  791. X    }
  792. X
  793. X    /* check if we're in concat output mode */
  794. X    if ( Concat ) {
  795. X    concat_it() ;
  796. X    return(0) ;
  797. X    }
  798. X
  799. X    /* check if any numbers were input */
  800. X    if ( !found_digit ) {
  801. X    /* entirely alphabetic input, so translate to number */
  802. X    for ( i = 0 ; i < NumDigits ; i++ ) {
  803. X        val = Letter2Numeral[PhoneStr[i]-'a'] ;
  804. X        if ( val != NO_NUMBER ) {
  805. X        fprintf( stdout, "%d", val ) ;
  806. X        } else {
  807. X        fprintf( stdout, "*" ) ;
  808. X        }
  809. X    }
  810. X    fprintf( stdout, "\n" ) ;
  811. X    return(0) ;
  812. X    }
  813. X
  814. X    if ( tchr == '*' ) {
  815. X    /* the phone number ends in a wild card - phrases can be longer */
  816. X    Wildcard = 1 ;
  817. X    while ( *tstr++ == '*' ) {
  818. X        Wildcard++ ;
  819. X    }
  820. X    } else {
  821. X    Wildcard = 0 ;
  822. X    }
  823. X
  824. X    if ( NumDigits > MAX_DIGITS ) {
  825. X    fprintf(stderr, "Sorry, too many digits input in `%s'; ",
  826. X        numeral_string);
  827. X    fprintf(stderr, "only %d allowed.\n", MAX_DIGITS);
  828. X    usage() ;
  829. X    return(0) ;
  830. X    }
  831. X
  832. X    /* set up offset array for indexing WL */
  833. X    tot = 0 ;
  834. X    for ( i = 0 ; i < NumDigits ; i++ ) {
  835. X    WLoffset[i] = tot ;
  836. X    tot += (NumDigits-i) ;
  837. X    }
  838. X    NumIndices = ((NumDigits+1)*NumDigits)/2 ;
  839. X
  840. X    /* build WL */
  841. X    word_count = 0 ;
  842. X    init_wl() ;
  843. X    dp = DictPath ;
  844. X    i = DictTot ;
  845. X    while ( i-- ) {
  846. X    infile = fopen( *dp, "r") ;
  847. X    min_length = MinLength > 1 ? MinLength : 2 ;
  848. X    if ( infile != NULL ) {
  849. X        while ( fgets( dict_word, BUFSIZE, infile ) ) {
  850. X        word_count += fit_word( dict_word, min_length ) ;
  851. X        }
  852. X    } else {
  853. X        fprintf( stderr, "Sorry, couldn't find dictionary at `%s'.\n",
  854. X            *dp ) ;
  855. X        return(1) ;
  856. X    }
  857. X    fclose( infile ) ;
  858. X    dp++ ;
  859. X    }
  860. X
  861. X    if ( Verbose ) {
  862. X    for ( i = 0 ; i < NumDigits ; i++ ) {
  863. X        for ( length = 1 ; length <= NumDigits-i ; length++ ) {
  864. X        p_wl = &WL[IndexWL( i, length )] ;
  865. X        if ( p_wl->count ) {
  866. X            fprintf( stdout, "\nDigit %d, length %d:\n", i+1, length ) ;
  867. X            for ( tot = 0 ; tot < p_wl->count ; tot++ ) {
  868. X            fprintf( stdout, " %s\n", p_wl->p_word[tot] ) ;
  869. X            }
  870. X        }
  871. X        }
  872. X    }
  873. X    }
  874. X
  875. X    MatchTot = 0 ;    /* number of words matched */
  876. X
  877. X    /* save NumNumerals around in case it gets incremented */
  878. X    nn = NumNumerals ;
  879. X
  880. X    /* search through WL for paths */
  881. X    if ( word_count ) {
  882. X    /* good, there's something to work with: search away! */
  883. X    *out_string = '\0' ;
  884. X    while ( !MatchTot && ( NumNumerals <= NumDigits ) ) {
  885. X        search_for_match( 0, out_string, out_string, 0 ) ;
  886. X        if ( !MatchTot ) {
  887. X        fprintf( stderr, "No matches with -n %d", NumNumerals ) ;
  888. X        if ( NumNumerals < NumDigits ) {
  889. X            NumNumerals++ ;
  890. X            fprintf( stderr,
  891. X                ", trying again with -n %d\n", NumNumerals ) ;
  892. X        } else {
  893. X            fprintf( stderr, "\n" ) ;
  894. X        }
  895. X        }
  896. X    }
  897. X    if ( !MatchTot ) {
  898. X        fprintf( stderr,
  899. X    "Bad luck: dictionary words fit, but there's too much numeral goop.\n");
  900. X        fprintf( stderr,
  901. X"Maybe try again with the option `-s abcdgimoptuxy248' or try wildcarding.\n"
  902. X            ) ;
  903. X    }
  904. X    } else {
  905. X    fprintf( stderr,
  906. X        "Worst luck: no dictionary words fit anywhere in %s.\n",
  907. X        PhoneStr ) ;
  908. X    fprintf( stderr,
  909. X"Maybe try again with the option `-s abcdgimoptuxy248' or try wildcarding.\n"
  910. X            ) ;
  911. X    }
  912. X
  913. X    /* restore NumNumerals (yeah, this is sloppy - sue me) */
  914. X    NumNumerals = nn ;
  915. X
  916. X    free_wl() ;
  917. X
  918. X    return(0) ;
  919. X}
  920. X
  921. Xvoid
  922. Xroll_own()
  923. X{
  924. Xint    digit_letter[MAX_DIGITS], found_one, search, i, j, k ;
  925. X
  926. X    for ( i = 0 ; i < NumDigits ; i++ ) {
  927. X    if ( PhoneNum[i] == FIXED_LETTER ) {
  928. X        digit_letter[i] = -2 ;
  929. X    } else {
  930. X        digit_letter[i] = -1 ;
  931. X    }
  932. X    }
  933. X    found_one = TRUE ;
  934. X    for ( i = 0 ; i < 26 && found_one ; i++ ) {
  935. X    found_one = FALSE ;
  936. X    for ( j = 0 ; j < NumDigits ; j++ ) {
  937. X        if ( digit_letter[j] >= 26 ) {
  938. X        /* done with this one */
  939. X        fprintf( stdout, "   " ) ;
  940. X        } else {
  941. X        if ( digit_letter[j] == -2 ) {
  942. X            found_one = TRUE ;
  943. X            digit_letter[j] = 26 ;
  944. X            fprintf( stdout, "  %c", PhoneStr[j] ) ;
  945. X        } else {
  946. X            for ( k = digit_letter[j] + 1, search = TRUE
  947. X            ; k < 26 && search
  948. X            ; k++ ) {
  949. X
  950. X            if ( Letter2Numeral[k] == PhoneNum[j] ) {
  951. X                search = FALSE ;
  952. X                found_one = TRUE ;
  953. X                fprintf( stdout, "  %c", (char)(k+'a') ) ;
  954. X                digit_letter[j] = k ;
  955. X            }
  956. X            }
  957. X            if ( search ) {
  958. X            /* no further match found */
  959. X            if ( digit_letter[j] < 0 ) {
  960. X                /* numeral is not mapped, so output it directly */
  961. X                fprintf( stdout, "  %c", PhoneStr[j] ) ;
  962. X            }
  963. X            digit_letter[j] = 26 ;
  964. X            }
  965. X        }
  966. X        }
  967. X    }
  968. X    fprintf( stdout, "\n" ) ;
  969. X    }
  970. X}
  971. X
  972. Xvoid
  973. Xconcat_it()
  974. X{
  975. Xint    i, j, tot ;
  976. Xchar    full_string[MAX_DIGITS+1] ;
  977. X
  978. X    for ( i = 0 ; i < NumDigits ; i++ ) {
  979. X    if ( PhoneNum[i] == FIXED_LETTER ) {
  980. X        OutputGrid[i][0] = PhoneStr[i] ;
  981. X        OutputGrid[i][1] = '\0' ;
  982. X    } else {
  983. X        tot = 0 ;
  984. X        for ( j = 0 ; j < 26 ; j++ ) {
  985. X        if ( Letter2Numeral[j] == PhoneNum[i] ) {
  986. X            OutputGrid[i][tot++] = (char)(j+'a') ;
  987. X        }
  988. X        }
  989. X        if ( !tot ) {
  990. X        /* no match found */
  991. X        OutputGrid[i][tot++] = PhoneStr[i] ;
  992. X        }
  993. X        OutputGrid[i][tot] = '\0' ;
  994. X    }
  995. X    }
  996. X
  997. X    if ( Concat == 1 ) {
  998. X    concat_letter_out( 0, full_string ) ;
  999. X    } else {
  1000. X    concat_letter_breaks_out( 0, full_string ) ;
  1001. X    }
  1002. X}
  1003. X
  1004. Xvoid
  1005. Xconcat_letter_out( digit, full_string )
  1006. Xint    digit ;
  1007. Xchar    *full_string ;
  1008. X{
  1009. Xint    i, length ;
  1010. Xchar    *cstr ;
  1011. X
  1012. X    if ( digit >= NumDigits ) {
  1013. X    full_string[digit] = '\0' ;
  1014. X    fprintf( stdout, "%s\n", full_string ) ;
  1015. X    } else {
  1016. X    length = strlen( cstr = OutputGrid[digit] ) ;
  1017. X    for ( i = 0 ; i < length ; i++ ) {
  1018. X        full_string[digit] = *cstr++ ;
  1019. X        concat_letter_out( digit+1, full_string ) ;
  1020. X    }
  1021. X    }
  1022. X}
  1023. X
  1024. Xvoid
  1025. Xconcat_letter_breaks_out( digit, full_string )
  1026. Xint    digit ;
  1027. Xchar    *full_string ;
  1028. X{
  1029. Xchar    brk_string[MAX_DIGITS*2] ;
  1030. Xint    i, j, bny, index, length ;
  1031. Xchar    *cstr ;
  1032. X
  1033. X    if ( digit >= NumDigits ) {
  1034. X    if ( TotBrk == -1 ) {
  1035. X        TotBrk = 1 ;
  1036. X        for ( i = 1 ; i < NumDigits ; i++ ) {
  1037. X        TotBrk *= 2 ;
  1038. X        }
  1039. X    }
  1040. X    for ( i = 0 ; i < TotBrk ; i++ ) {
  1041. X        bny = i ;
  1042. X        index = 0 ;
  1043. X        for ( j = 0 ; j < NumDigits ; j++ ) {
  1044. X        brk_string[index++] = full_string[j] ;
  1045. X        if ( bny & 0x1 ) {
  1046. X            brk_string[index++] = ' ' ;
  1047. X        }
  1048. X        bny = bny >> 1 ;
  1049. X        }
  1050. X        brk_string[index] = '\0' ;
  1051. X        fprintf( stdout, "%s\n", brk_string ) ;
  1052. X    }
  1053. X    } else {
  1054. X    length = strlen( cstr = OutputGrid[digit] ) ;
  1055. X    for ( i = 0 ; i < length ; i++ ) {
  1056. X        full_string[digit] = *cstr++ ;
  1057. X        concat_letter_breaks_out( digit+1, full_string ) ;
  1058. X    }
  1059. X    }
  1060. X}
  1061. X
  1062. X/* create structures needed for the word list, etc */
  1063. Xvoid
  1064. Xinit_wl()
  1065. X{
  1066. Xp_wordlist p_wl ;
  1067. Xint    i, j, length, search ;
  1068. Xchar    tstr[2] ;
  1069. X
  1070. X    for ( i = 0, p_wl = WL ; i < NumIndices ; i++, p_wl++ ) {
  1071. X    p_wl->p_word = NULL ;
  1072. X    p_wl->count = 0 ;
  1073. X    p_wl->size = 0 ;
  1074. X    }
  1075. X
  1076. X    /* figure out which numerals don't have any letter translations */
  1077. X    for ( i = 0 ; i < 10 ; i++ ) {
  1078. X    for ( j = 0, search = TRUE ; j < 26 && search ; j++ ) {
  1079. X        if ( i == Letter2Numeral[j] ) {
  1080. X        search = FALSE ;
  1081. X        }
  1082. X    }
  1083. X    /* set to TRUE if numeral is mapped by something */
  1084. X    NumeralMapped[i] = !search ;
  1085. X    }
  1086. X
  1087. X    tstr[1] = '\0' ;
  1088. X    /* add one number values as possible */
  1089. X    /* we rely on the fact that the first word in the single letter word
  1090. X     * lists is actually a numeral, so change the following at your own
  1091. X     * peril.
  1092. X     */
  1093. X    for ( i = 0 ; i < 10 ; i++ ) {
  1094. X    tstr[0] = (char)i + '0' ;
  1095. X    (void)fit_word( tstr, 1 ) ;
  1096. X    }
  1097. X
  1098. X    /* add one letter words as possible */
  1099. X    length = strlen( OneLetter ) ;
  1100. X    for ( i = 0 ; i < length ; i++ ) {
  1101. X    tstr[0] = OneLetter[i] ;
  1102. X    (void)fit_word( tstr, 1 ) ;
  1103. X    }
  1104. X}
  1105. X
  1106. X/* see if the word fits in the database anywhere */
  1107. Xint
  1108. Xfit_word( dict_word, min_length )
  1109. Xchar    *dict_word ;
  1110. Xint    min_length ;
  1111. X{
  1112. Xint    length, compare_length, true_length, index_length, nl ;
  1113. Xint    i, j, num_match ;
  1114. Xint    word_val[MAX_DIGITS], hit[MAX_DIGITS], *wv, check_length, match, tot ;
  1115. Xchar    lc_dict_word[MAX_DIGITS], *wc, *lc, tchr, *cc ;
  1116. Xchar    clean_dict_word[BUFSIZE], new_word[BUFSIZE] ;
  1117. X
  1118. X    /* set new_word to empty string to initialize permute_word */
  1119. X    tot = 0 ;
  1120. X    *new_word = '\0' ;
  1121. X    while( permute_word( dict_word, new_word ) ) {
  1122. X    /* convert word to numbers */
  1123. X    length = strlen( new_word ) ;
  1124. X    compare_length = true_length = 0 ;
  1125. X    for ( i = 0, wv = word_val, wc = new_word, lc = lc_dict_word,
  1126. X          cc = clean_dict_word
  1127. X        ; ( i < length ) && ( compare_length < NumDigits )
  1128. X        ; i++, wc++ ) {
  1129. X
  1130. X        /* remove apostrophes, hyphens, etc for matching */
  1131. X        tchr = tolower(*wc) ;
  1132. X        if ( ( tchr >= 'a' ) && ( tchr <= 'z' ) ) {
  1133. X        *wv++ = Letter2Numeral[(*lc++ = tchr) -'a'] ;
  1134. X        compare_length++ ;
  1135. X        } else if ( ( tchr >= '0' ) && ( tchr <= '9' ) ) {
  1136. X        *wv++ = (*lc++ = tchr) -'0' ;
  1137. X        compare_length++ ;
  1138. X        }
  1139. X        *cc++ = *wc ;
  1140. X    }
  1141. X    true_length = compare_length ;
  1142. X
  1143. X    while ( *wc ) {
  1144. X        tchr = tolower( *wc++ ) ;
  1145. X        /* get true length of word in valid characters */
  1146. X        if ( ( tchr >= 'a' ) && ( tchr <= 'z' ) ) {
  1147. X        true_length++ ;
  1148. X        } else if ( ( tchr >= '0' ) && ( tchr <= '9' ) ) {
  1149. X        true_length++ ;
  1150. X        }
  1151. X        /* copy the rest of the word */
  1152. X        *cc++ = tchr ;
  1153. X    }
  1154. X    /* end the cleaned word */
  1155. X    *cc = '\0' ;
  1156. X
  1157. X    /* is the word too long? */
  1158. X    if ( true_length > NumDigits + Wildcard ) {
  1159. X        /* the word was too long: for ispell words we know that the
  1160. X         * first word is going to be as short as it gets, so we bail
  1161. X         * out of successive word generation here now.
  1162. X         */
  1163. X        goto LastWord ;
  1164. X    }
  1165. X
  1166. X    /* is the word too short? */
  1167. X    if ( true_length < min_length ) {
  1168. X        /* the word was too short */
  1169. X        goto NextWord ;
  1170. X    }
  1171. X
  1172. X    /* now look for matches */
  1173. X    if ( Wildcard ) {
  1174. X        check_length = NumDigits - true_length + Wildcard ;
  1175. X        if ( check_length >= NumDigits ) {
  1176. X        /* cannot index into array at higher than NumDigits-1,
  1177. X         * so reduce length.
  1178. X         */
  1179. X        check_length = NumDigits - 1 ;
  1180. X        }
  1181. X    } else {
  1182. X        check_length = NumDigits - true_length ;
  1183. X    }
  1184. X
  1185. X    for ( i = 0, num_match = 0 ; i <= check_length ; i++ ) {
  1186. X        index_length = compare_length+i ;
  1187. X        if ( Wildcard ) {
  1188. X        if ( index_length > NumDigits ) {
  1189. X            /* cut comparisons down to numerals available */
  1190. X            index_length = NumDigits ;
  1191. X        }
  1192. X        }
  1193. X        for ( j = i, wv = word_val, match = TRUE
  1194. X        ; match && (j < index_length)
  1195. X        ; j++, wv++ ) {
  1196. X
  1197. X        /* numerical match? */
  1198. X        if ( PhoneNum[j] != *wv ) {
  1199. X            /* no; exact letter match? */
  1200. X            if ( ( PhoneNum[j] != FIXED_LETTER ) ||
  1201. X             ( PhoneStr[j] != lc_dict_word[j-i] ) ) {
  1202. X            /* no match, stop testing */
  1203. X            match = FALSE ;
  1204. X            }
  1205. X        }
  1206. X        }
  1207. X        if ( match ) {
  1208. X        /* word fits, store index */
  1209. X        hit[num_match++] = i ;
  1210. X        }
  1211. X    }
  1212. X    /* were there any matches? */
  1213. X    if ( num_match ) {
  1214. X        /* make one copy of the word */
  1215. X        wc = str_duplicate( clean_dict_word ) ;
  1216. X        if ( !wc ) {
  1217. X        fprintf( stderr, "Ugh, we're out of memory!\n" ) ;
  1218. X        exit(1) ;
  1219. X        }
  1220. X        hold_word( wc ) ;
  1221. X        for ( i = 0 ; i < num_match ; i++ ) {
  1222. X        if ( Wildcard ) {
  1223. X            if ( hit[i] + true_length <= NumDigits ) {
  1224. X            nl = true_length ;
  1225. X            } else {
  1226. X            nl = NumDigits - hit[i] ;
  1227. X            }
  1228. X            if ( !store_word( wc, hit[i], nl ) ) {
  1229. X            /* duplicate word, so free it and go to next word */
  1230. X            free( wc ) ;
  1231. X            goto NextWord ;
  1232. X            }
  1233. X        } else {
  1234. X            if ( !store_word( wc, hit[i], true_length ) ) {
  1235. X            /* duplicate word, so free it and go to next word */
  1236. X            free( wc ) ;
  1237. X            goto NextWord ;
  1238. X            }
  1239. X        }
  1240. X        }
  1241. X        /* got here, so the word must have been stored */
  1242. X        tot++ ;
  1243. X    }
  1244. X    NextWord: ;
  1245. X    }
  1246. X    LastWord: ;
  1247. X    return( tot ) ;
  1248. X}
  1249. X
  1250. X/* take permutations of word if "/" GNU ispell format detected, else just
  1251. X * strip out invalid characters and return new word
  1252. X */
  1253. Xint
  1254. Xpermute_word( dict_word, new_word )
  1255. Xchar    *dict_word ;
  1256. Xchar    *new_word ;
  1257. X{
  1258. Xint    search_word ;
  1259. Xchar    *ppc, *psc, *dc, *wc, tchr, ttchr ;
  1260. Xstatic char perm_word[BUFSIZE], perm_pre_word[BUFSIZE] ;
  1261. Xstatic char perm_pre_cmd[BUFSIZE], perm_suf_cmd[BUFSIZE] ;
  1262. Xstatic int  perm_pre_count, perm_suf_count ;
  1263. Xstatic int  perm_cur_pre_count, perm_cur_suf_count, pre_length ;
  1264. X
  1265. X    if ( *new_word ) {
  1266. X    /* on successive call of this routine by calling procedure, so get
  1267. X     * a permutation (if available) and return it.
  1268. X     */
  1269. X
  1270. X    /* subtract previous output permutation from count and see if done */
  1271. X    search_word = 0 ;
  1272. X    do {
  1273. X        if ( perm_cur_suf_count <= 0 ) {
  1274. X        if ( perm_cur_pre_count <= 0 ) {
  1275. X            /* no more permutations */
  1276. X            return( 0 ) ;
  1277. X        }
  1278. X        perm_cur_suf_count = perm_suf_count ;
  1279. X        switch( perm_pre_cmd[--perm_cur_pre_count] ) {
  1280. X            case 'A':    /* enter -> reenter */
  1281. X            strcpy( perm_pre_word, "re" ) ;
  1282. X            strcat( perm_pre_word, perm_word ) ;
  1283. X            break ;
  1284. X            case 'I':    /* disposed -> indisposed */
  1285. X            strcpy( perm_pre_word, "re" ) ;
  1286. X            strcat( perm_pre_word, perm_word ) ;
  1287. X            break ;
  1288. X            case 'U':    /* natural -> unnatural */
  1289. X            strcpy( perm_pre_word, "un" ) ;
  1290. X            strcat( perm_pre_word, perm_word ) ;
  1291. X            break ;
  1292. X            default:
  1293. X            fprintf( stderr,
  1294. X            "warning:  unrecognized word flag `%c' for word `%s'\n",
  1295. X                tchr, perm_word ) ;
  1296. X            return( 0 ) ;
  1297. X        }
  1298. X        pre_length = strlen( perm_pre_word ) ;
  1299. X        /* for the first prefix addition just return rest of word
  1300. X         * without any suffix
  1301. X         */
  1302. X        strcpy( new_word, perm_pre_word ) ;
  1303. X        } else {
  1304. X        strcpy( new_word, perm_pre_word ) ;
  1305. X        switch( perm_suf_cmd[--perm_cur_suf_count] ) {
  1306. X            case 'V':    /* create -> creative */
  1307. X            /* use only when there is no prefix */
  1308. X            if ( perm_cur_pre_count == perm_pre_count ) {
  1309. X                tchr = tolower(new_word[pre_length-1]) ;
  1310. X                if ( tchr == 'e' ) {
  1311. X                new_word[pre_length-1] = '\0' ;
  1312. X                }
  1313. X                strcat( new_word, "ive" ) ;
  1314. X            } else {
  1315. X                /* invalid combination, so continue */
  1316. X                search_word = 1 ;
  1317. X            }
  1318. X            break ;
  1319. X            case 'N':    /* create -> creation */
  1320. X            tchr = tolower(new_word[pre_length-1]) ;
  1321. X            if ( tchr == 'e' ) {
  1322. X                new_word[pre_length-1] = '\0' ;
  1323. X                strcat( new_word, "ion" ) ;
  1324. X            } else if ( tchr == 'y' ) {
  1325. X                new_word[pre_length-1] = '\0' ;
  1326. X                strcat( new_word, "ication" ) ;
  1327. X            } else {
  1328. X                strcat( new_word, "en" ) ;
  1329. X            }
  1330. X            break ;
  1331. X            case 'X':    /* create -> creations */
  1332. X            tchr = tolower(new_word[pre_length-1]) ;
  1333. X            if ( tchr == 'e' ) {
  1334. X                new_word[pre_length-1] = '\0' ;
  1335. X                strcat( new_word, "ions" ) ;
  1336. X            } else if ( tchr == 'y' ) {
  1337. X                new_word[pre_length-1] = '\0' ;
  1338. X                strcat( new_word, "ications" ) ;
  1339. X            } else {
  1340. X                strcat( new_word, "ens" ) ;
  1341. X            }
  1342. X            break ;
  1343. X            case 'H':    /* twenty -> twentieth */
  1344. X            /* use only when there is no prefix */
  1345. X            if ( perm_cur_pre_count == perm_pre_count ) {
  1346. X                tchr = tolower(new_word[pre_length-1]) ;
  1347. X                if ( tchr == 'y' ) {
  1348. X                new_word[pre_length-1] = '\0' ;
  1349. X                strcat( new_word, "ieth" ) ;
  1350. X                } else {
  1351. X                strcat( new_word, "th" ) ;
  1352. X                }
  1353. X            } else {
  1354. X                /* invalid combination, so continue */
  1355. X                search_word = 1 ;
  1356. X            }
  1357. X            break ;
  1358. X            case 'Y':    /* quick -> quickly */
  1359. X            strcat( new_word, "ly" ) ;
  1360. X            break ;
  1361. X            case 'G':    /* file -> filing */
  1362. X            tchr = tolower(new_word[pre_length-1]) ;
  1363. X            if ( tchr == 'e' ) {
  1364. X                new_word[pre_length-1] = '\0' ;
  1365. X            }
  1366. X            strcat( new_word, "ing" ) ;
  1367. X            break ;
  1368. X            case 'J':    /* file -> filings */
  1369. X            tchr = tolower(new_word[pre_length-1]) ;
  1370. X            if ( tchr == 'e' ) {
  1371. X                new_word[pre_length-1] = '\0' ;
  1372. X            }
  1373. X            strcat( new_word, "ings" ) ;
  1374. X            break ;
  1375. X            case 'D':    /* create -> created */
  1376. X            tchr = tolower(new_word[pre_length-1]) ;
  1377. X            if ( tchr == 'e' ) {
  1378. X                strcat( new_word, "d" ) ;
  1379. X            } else if ( tchr == 'y' ) {
  1380. X                ttchr = tolower(new_word[pre_length-2]) ;
  1381. X                if ( Vowel(ttchr) ) {
  1382. X                strcat( new_word, "ed" ) ;
  1383. X                } else {
  1384. X                new_word[pre_length-1] = '\0' ;
  1385. X                strcat( new_word, "ied" ) ;
  1386. X                }
  1387. X            } else {
  1388. X                strcat( new_word, "ed" ) ;
  1389. X            }
  1390. X            break ;
  1391. X            case 'T':    /* late -> latest */
  1392. X            /* use only when there is no prefix */
  1393. X            if ( perm_cur_pre_count == perm_pre_count ) {
  1394. X                tchr = tolower(new_word[pre_length-1]) ;
  1395. X                if ( tchr == 'e' ) {
  1396. X                strcat( new_word, "st" ) ;
  1397. X                } else if ( tchr == 'y' ) {
  1398. X                ttchr = tolower(new_word[pre_length-2]) ;
  1399. X                if ( Vowel(ttchr) ) {
  1400. X                    strcat( new_word, "est" ) ;
  1401. X                } else {
  1402. X                    new_word[pre_length-1] = '\0' ;
  1403. X                    strcat( new_word, "iest" ) ;
  1404. X                }
  1405. X                } else {
  1406. X                strcat( new_word, "est" ) ;
  1407. X                }
  1408. X            } else {
  1409. X                /* invalid combination, so continue */
  1410. X                search_word = 1 ;
  1411. X            }
  1412. X            break ;
  1413. X            case 'R':    /* late -> later */
  1414. X            tchr = tolower(new_word[pre_length-1]) ;
  1415. X            if ( tchr == 'e' ) {
  1416. X                strcat( new_word, "r" ) ;
  1417. X            } else if ( tchr == 'y' ) {
  1418. X                ttchr = tolower(new_word[pre_length-2]) ;
  1419. X                if ( Vowel(ttchr) ) {
  1420. X                strcat( new_word, "er" ) ;
  1421. X                } else {
  1422. X                new_word[pre_length-1] = '\0' ;
  1423. X                strcat( new_word, "ier" ) ;
  1424. X                }
  1425. X            } else {
  1426. X                strcat( new_word, "er" ) ;
  1427. X            }
  1428. X            break ;
  1429. X            case 'Z':    /* skate ->skaters */
  1430. X            tchr = tolower(new_word[pre_length-1]) ;
  1431. X            if ( tchr == 'e' ) {
  1432. X                strcat( new_word, "rs" ) ;
  1433. X            } else if ( tchr == 'y' ) {
  1434. X                ttchr = tolower(new_word[pre_length-2]) ;
  1435. X                if ( Vowel(ttchr) ) {
  1436. X                strcat( new_word, "ers" ) ;
  1437. X                } else {
  1438. X                new_word[pre_length-1] = '\0' ;
  1439. X                strcat( new_word, "iers" ) ;
  1440. X                }
  1441. X            } else {
  1442. X                strcat( new_word, "ers" ) ;
  1443. X            }
  1444. X            break ;
  1445. X            case 'S':    /* imply -> implies */
  1446. X            tchr = tolower(new_word[pre_length-1]) ;
  1447. X            if ( tchr == 'y' ) {
  1448. X                ttchr = tolower(new_word[pre_length-2]) ;
  1449. X                if ( Vowel(ttchr) ) {
  1450. X                strcat( new_word, "s" ) ;
  1451. X                } else {
  1452. X                new_word[pre_length-1] = '\0' ;
  1453. X                strcat( new_word, "ies" ) ;
  1454. X                }
  1455. X            } else {
  1456. X                if ( strchr( "sxzh", tchr ) ) {
  1457. X                strcat( new_word, "es" ) ;
  1458. X                } else {
  1459. X                strcat( new_word, "s" ) ;
  1460. X                }
  1461. X            }
  1462. X            break ;
  1463. X            case 'P':    /* cloudy -> cloudiness */
  1464. X            tchr = tolower(new_word[pre_length-1]) ;
  1465. X            if ( tchr == 'y' ) {
  1466. X                ttchr = tolower(new_word[pre_length-2]) ;
  1467. X                if ( Vowel(ttchr) ) {
  1468. X                strcat( new_word, "ness" ) ;
  1469. X                } else {
  1470. X                new_word[pre_length-1] = '\0' ;
  1471. X                strcat( new_word, "iness" ) ;
  1472. X                }
  1473. X            } else {
  1474. X                strcat( new_word, "ness" ) ;
  1475. X            }
  1476. X            break ;
  1477. X            case 'M':    /* dog -> dog's */
  1478. X            strcat( new_word, "'s" ) ;
  1479. X            break ;
  1480. X            default:
  1481. X            fprintf( stderr,
  1482. X            "warning:  unrecognized word flag `%c' for word `%s'\n",
  1483. X                tchr, perm_word ) ;
  1484. X            return( 0 ) ;
  1485. X        }
  1486. X        }
  1487. X    } while ( search_word ) ;
  1488. X    } else {
  1489. X    /* first call, so check if we need to permute */
  1490. X    if ( wc = strchr( dict_word, '/' ) ) {
  1491. X        /* GNU ispell format detected, so save word and permutations */
  1492. X        strncpy( perm_word, dict_word, wc-dict_word ) ;
  1493. X        perm_word[wc-dict_word] = '\0' ;
  1494. X
  1495. X        wc++ ;
  1496. X        ppc = perm_pre_cmd ;
  1497. X        psc = perm_suf_cmd ;
  1498. X        perm_pre_count = 0 ;
  1499. X        perm_suf_count = 0 ;
  1500. X        while ( tchr = toupper( *wc++ ) ) {
  1501. X        /* save only permutation characters */
  1502. X        if ( ( tchr >= 'A' ) && ( tchr <= 'Z' ) ) {
  1503. X            if ( (tchr == 'A') || (tchr == 'I') || (tchr == 'U') ) {
  1504. X            *ppc++ = tchr ;
  1505. X            perm_pre_count++ ;
  1506. X            } else {
  1507. X            *psc++ = tchr ;
  1508. X            perm_suf_count++ ;
  1509. X            }
  1510. X        }
  1511. X        }
  1512. X        *ppc = '\0' ;
  1513. X        *psc = '\0' ;
  1514. X        perm_cur_pre_count = perm_pre_count ;
  1515. X        perm_cur_suf_count = perm_suf_count ;
  1516. X        strcpy( perm_pre_word, perm_word ) ;
  1517. X        pre_length = strlen( perm_pre_word ) ;
  1518. X
  1519. X        /* and finally, copy the basic word for use */
  1520. X        strcpy( new_word, perm_word ) ;
  1521. X    } else {
  1522. X        /* no permutation, so set permutation list to empty */
  1523. X        perm_cur_pre_count = 0 ;
  1524. X        perm_cur_suf_count = 0 ;
  1525. X
  1526. X        /* cull out line feeds, etc */
  1527. X        wc = dict_word ;
  1528. X        dc = new_word ;
  1529. X        while ( tchr = *wc++ ) {
  1530. X        if ( tchr > 13 ) {
  1531. X            *dc++ = tchr ;
  1532. X        }
  1533. X        }
  1534. X        *dc = '\0' ;
  1535. X    }
  1536. X    }
  1537. X    return( 1 ) ;
  1538. X}
  1539. X
  1540. X/* yeah, I could combine this with store_word - call me lazy... */
  1541. Xvoid
  1542. Xhold_word( word )
  1543. Xchar    *word ;
  1544. X{
  1545. X    if ( HoldSize <= HoldCount ) {
  1546. X    if ( HoldSize ) {
  1547. X        HoldSize += WORD_LIST_SIZE * NumDigits ;
  1548. X        HoldWord = (char **)realloc( (void *)HoldWord,
  1549. X            HoldSize * sizeof(char *)) ;
  1550. X    } else {
  1551. X        HoldSize = WORD_LIST_SIZE * NumDigits ;
  1552. X        HoldWord =
  1553. X            (char **)malloc( HoldSize * sizeof(char *)) ;
  1554. X    }
  1555. X    if ( HoldWord == NULL ) {
  1556. X        fprintf( stderr, "Ugh, we're out of memory!\n" ) ;
  1557. X        exit(1) ;
  1558. X    }
  1559. X    }
  1560. X    HoldWord[HoldCount++] = word ;
  1561. X}
  1562. X
  1563. X/* store word in the given location, return 1 if word not a duplicate */
  1564. Xint
  1565. Xstore_word( word, digit, length )
  1566. Xchar    *word ;
  1567. Xint    digit ;
  1568. Xint    length ;
  1569. X{
  1570. Xp_wordlist    p_wl ;
  1571. Xint    i ;
  1572. X
  1573. X    p_wl = &WL[IndexWL( digit, length )] ;
  1574. X    /* check if word is already on list */
  1575. X    for ( i = 0 ; i < p_wl->count ; i++ ) {
  1576. X    if ( !strcmp( word, p_wl->p_word[i] ) ) {
  1577. X        /* duplicate word - don't store it */
  1578. X        return( 0 ) ;
  1579. X    }
  1580. X    }
  1581. X
  1582. X    /* check storage space */
  1583. X    if ( p_wl->size <= p_wl->count ) {
  1584. X    if ( p_wl->size ) {
  1585. X        p_wl->size += WORD_LIST_SIZE ;
  1586. X        p_wl->p_word = (char **)realloc( (void *)p_wl->p_word,
  1587. X            p_wl->size * sizeof(char *)) ;
  1588. X    } else {
  1589. X        p_wl->size = WORD_LIST_SIZE ;
  1590. X        p_wl->p_word =
  1591. X            (char **)malloc( p_wl->size * sizeof(char *)) ;
  1592. X    }
  1593. X    if ( p_wl->p_word == NULL ) {
  1594. X        fprintf( stderr, "Ugh, we're out of memory!\n" ) ;
  1595. X        exit(1) ;
  1596. X    }
  1597. X    }
  1598. X    /* store word in structure */
  1599. X    p_wl->p_word[p_wl->count++] = word ;
  1600. X
  1601. X    return( 1 ) ;
  1602. X}
  1603. X
  1604. X/* search through the stored words for matches */
  1605. Xvoid
  1606. Xsearch_for_match( digit, full_string, suffix_loc, numeral_count )
  1607. Xint    digit ;
  1608. Xchar    *full_string ;
  1609. Xchar    *suffix_loc ;
  1610. Xint    numeral_count ;
  1611. X{
  1612. Xint    length, tot_len, tot_word, wn, add_num, val ;
  1613. Xp_wordlist    p_wl ;
  1614. Xchar    **p_word ;
  1615. X
  1616. X    tot_len = NumDigits - digit ;
  1617. X    /* loop through all possible word lengths from this point */
  1618. X    /* count down so that the longer strings are output first */
  1619. X    for ( length = tot_len ; length > 0 ; length-- ) {
  1620. X    p_wl = &WL[IndexWL( digit, length )] ;
  1621. X    tot_word = p_wl->count ;
  1622. X    /* now go through all words on the list */
  1623. X    if ( ( length == 1 ) && tot_word ) {
  1624. X        val = **(p_wl->p_word) - '0' ;
  1625. X        /* is the first word a numeral, and is it mapped? */
  1626. X        if ( ( val >= 0 ) && ( val <= 9 ) && NumeralMapped[val] ) {
  1627. X        /* don't include first word (a mapped numeral) if we've used
  1628. X         * up our quota.
  1629. X         */
  1630. X        wn = (numeral_count >= NumNumerals ) ;
  1631. X        add_num = 1 ;
  1632. X        } else {
  1633. X        /* an unmapped numeral, so definitely do it */
  1634. X        wn = 0 ;
  1635. X        add_num = 0 ;
  1636. X        }
  1637. X    } else {
  1638. X        /* not on the one letter word list, so do all words */
  1639. X        wn = 0 ;
  1640. X        add_num = 0 ;
  1641. X    }
  1642. X    for ( p_word = p_wl->p_word + wn
  1643. X        ; wn < tot_word
  1644. X        ; wn++, p_word++ ) {
  1645. X
  1646. X        strcpy( suffix_loc, *p_word ) ;
  1647. X        if ( length == tot_len ) {
  1648. X        /* finished - output it! */
  1649. X        fprintf( stdout, "%s\n", full_string ) ;
  1650. X        MatchTot++ ;
  1651. X        } else {
  1652. X        strcat( suffix_loc, " " ) ;
  1653. X        /* Add one to numeral_count only if numeral is used */
  1654. X        search_for_match( digit+length, full_string,
  1655. X            suffix_loc + strlen( suffix_loc ),
  1656. X            numeral_count + add_num ) ;
  1657. X        }
  1658. X    }
  1659. X    }
  1660. X}
  1661. X
  1662. Xvoid
  1663. Xfree_wl()
  1664. X{
  1665. Xint i ;
  1666. Xp_wordlist p_wl ;
  1667. X
  1668. X    for ( i = 0, p_wl = WL ; i < NumIndices ; i++, p_wl++ ) {
  1669. X    if ( p_wl->size ) {
  1670. X        p_wl->size = 0 ;
  1671. X        p_wl->count = 0 ;
  1672. X        free( p_wl->p_word ) ;
  1673. X    }
  1674. X    }
  1675. X
  1676. X    /* the only function of HoldWord is to be able to free the word memory */
  1677. X    if ( HoldSize ) {
  1678. X    for ( i = 0 ; i < HoldCount ; i++ ) {
  1679. X        free( HoldWord[i] ) ;
  1680. X    }
  1681. X    free( HoldWord ) ;
  1682. X    }
  1683. X    HoldCount = HoldSize = 0 ;
  1684. X}
  1685. X
  1686. X/* strip out all valid "-" arguments and their values */
  1687. Xint
  1688. Xscan_options( argc, argv, nargc, nargv )
  1689. Xint    argc ;
  1690. Xchar    *argv[] ;
  1691. Xint    *nargc ;
  1692. Xchar    *nargv[] ;
  1693. X{
  1694. Xint    num_arg ;
  1695. Xint    i, first_dict ;
  1696. Xchar    str[BUFSIZE] ;
  1697. X
  1698. X    *nargc = num_arg = 0 ;
  1699. X    first_dict = 1 ;    /* haven't received a dictionary path yet */
  1700. X
  1701. X    while ( ++num_arg < argc ) {
  1702. X    if ( *argv[num_arg] == '-' ) {
  1703. X        switch( argv[num_arg][1] ) {
  1704. X        case 'l':    /* minimum length of words */
  1705. X            if ( ++num_arg < argc ) {
  1706. X            sscanf( argv[num_arg], "%d", &i ) ;
  1707. X            if ( i < 1 ) {
  1708. X                fprintf( stderr,
  1709. X                    "Minimum word length too low!\n" ) ;
  1710. X                usage() ;
  1711. X                return( FALSE ) ;
  1712. X            }
  1713. X            MinLength = i ;
  1714. X            } else {
  1715. X            fprintf( stderr, "No minimum length given for -l.\n" ) ;
  1716. X            usage() ;
  1717. X            return( FALSE ) ;
  1718. X            }
  1719. X            break ;
  1720. X        case 'n':    /* number of numerals allowed */
  1721. X            if ( ++num_arg < argc ) {
  1722. X            sscanf( argv[num_arg], "%d", &i ) ;
  1723. X            if ( i < 0 ) {
  1724. X                fprintf( stderr, "Number of numerals too low!\n" ) ;
  1725. X                usage() ;
  1726. X                return( FALSE ) ;
  1727. X            }
  1728. X            NumNumerals = i ;
  1729. X            } else {
  1730. X            fprintf( stderr,
  1731. X                "No number of numerals given for -n.\n" ) ;
  1732. X            usage() ;
  1733. X            return( FALSE ) ;
  1734. X            }
  1735. X            break ;
  1736. X        case 'q':    /* mapping of q */
  1737. X            if ( ++num_arg < argc ) {
  1738. X            sscanf( argv[num_arg], "%d", &i ) ;
  1739. X            if ( (i >= 0) && (i <= 9) ) {
  1740. X                Letter2Numeral['q'-'a'] = i ;
  1741. X            }
  1742. X            } else {
  1743. X            fprintf( stderr, "No mapped number given for -q.\n" ) ;
  1744. X            usage() ;
  1745. X            return( FALSE ) ;
  1746. X            }
  1747. X            break ;
  1748. X        case 'z':    /* mapping of z */
  1749. X            if ( ++num_arg < argc ) {
  1750. X            sscanf( argv[num_arg], "%d", &i ) ;
  1751. X            if ( (i >= 0) && (i <= 9) ) {
  1752. X                Letter2Numeral['z'-'a'] = i ;
  1753. X            }
  1754. X            } else {
  1755. X            fprintf( stderr, "No mapped number given for -z.\n" ) ;
  1756. X            usage() ;
  1757. X            return( FALSE ) ;
  1758. X            }
  1759. X            break ;
  1760. X        case 'd':    /* dictionary path */
  1761. X            if ( ++num_arg < argc ) {
  1762. X            if ( first_dict ) {
  1763. X                first_dict = 0 ;
  1764. X                /* erase first dictionary, since we're
  1765. X                 * overriding by giving a path.
  1766. X                 */
  1767. X                DictTot = 0 ;
  1768. X                free( *DictPath ) ;
  1769. X            }
  1770. X            sscanf( argv[num_arg], "%s", str ) ;
  1771. X            if ( (str[0] == '.') && ( str[1] == NULL ) ) {
  1772. X                /* for '.' use the default dictionary */
  1773. X                strcpy( str, DICT_PATH ) ;
  1774. X            }
  1775. X            DictPath[DictTot] = str_duplicate( str ) ;
  1776. X            if ( !DictPath[DictTot++] ) {
  1777. X                fprintf( stderr, "Ugh, we're out of memory!\n" ) ;
  1778. X                exit(1) ;
  1779. X            }
  1780. X            } else {
  1781. X            fprintf( stderr,
  1782. X                "No dictionary path given for -d.\n" ) ;
  1783. X            usage() ;
  1784. X            return( FALSE ) ;
  1785. X            }
  1786. X            break ;
  1787. X        case 's':    /* allowed single letter words */
  1788. X            if ( ++num_arg < argc ) {
  1789. X            sscanf( argv[num_arg], "%s", OneLetter ) ;
  1790. X            } else {
  1791. X            fprintf( stderr,
  1792. X                "No single letter words given for -s.\n" ) ;
  1793. X            usage() ;
  1794. X            return( FALSE ) ;
  1795. X            }
  1796. X            break ;
  1797. X        case 'm':    /* mapping of entire alphabet */
  1798. X            if ( ++num_arg < argc ) {
  1799. X            sscanf( argv[num_arg], "%s", str ) ;
  1800. X            if ( strlen(str) != 26 ) {
  1801. X                fprintf( stderr, "Must input all 26 digits\n" ) ;
  1802. X                usage() ;
  1803. X                return( FALSE ) ;
  1804. X            }
  1805. X            for ( i = 0 ; i < 26 ; i++ ) {
  1806. X                if ( (str[i] >= '0') && (str[i] <= '9') ) {
  1807. X                Letter2Numeral[i] = str[i] - '0' ;
  1808. X                } else {
  1809. X                Letter2Numeral[i] = NO_NUMBER ;
  1810. X                }
  1811. X            }
  1812. X            if ( Verbose ) {
  1813. X                fprintf( stderr, "here's your banana: )\n" ) ;
  1814. X            }
  1815. X            } else {
  1816. X            fprintf( stderr, "No digit map given for -m.\n" ) ;
  1817. X            usage() ;
  1818. X            return( FALSE ) ;
  1819. X            }
  1820. X            break ;
  1821. X        case 'r':    /* output concatenation */
  1822. X            RollOwn = TRUE ;
  1823. X            break ;
  1824. X        case 'c':    /* output concatenation */
  1825. X            Concat = 1 ;
  1826. X            break ;
  1827. X        case 'C':    /* output spacing & concatenation */
  1828. X            Concat = 2 ;
  1829. X            break ;
  1830. X        case 'v':    /* verbose output */
  1831. X            Verbose = TRUE ;
  1832. X            break ;
  1833. X        default:
  1834. X            fprintf( stderr, "No such option `-%c'.\n",
  1835. X                argv[num_arg][1] ) ;
  1836. X            usage() ;
  1837. X            return( FALSE ) ;
  1838. X        }
  1839. X    } else {
  1840. X        /* not an argument, so pass it on */
  1841. X        nargv[(*nargc)++] = argv[num_arg] ;
  1842. X    }
  1843. X    }
  1844. X    return( TRUE ) ;
  1845. X}
  1846. X
  1847. X/* strdup for the masses, since strdup is not standard */
  1848. Xchar *str_duplicate( s )
  1849. Xchar    *s ;
  1850. X{
  1851. Xchar *ps ;
  1852. X
  1853. X    if ( ps = malloc( strlen( s ) + 1 ) ) {
  1854. X    strcpy( ps, s ) ;
  1855. X    }
  1856. X    return( ps ) ;
  1857. X}
  1858. END_OF_FILE
  1859.   if test 35263 -ne `wc -c <'phonewrd.c'`; then
  1860.     echo shar: \"'phonewrd.c'\" unpacked with wrong size!
  1861.   fi
  1862.   # end of 'phonewrd.c'
  1863. fi
  1864. if test -f 'phonewrd.txt' -a "${1}" != "-c" ; then 
  1865.   echo shar: Will not clobber existing file \"'phonewrd.txt'\"
  1866. else
  1867.   echo shar: Extracting \"'phonewrd.txt'\" \(9737 characters\)
  1868.   sed "s/^X//" >'phonewrd.txt' <<'END_OF_FILE'
  1869. X
  1870. X
  1871. X
  1872. X PHONEWRD(1)                             PHONEWRD(1)
  1873. X
  1874. X
  1875. X
  1876. X
  1877. X NAME
  1878. X      phonewrd - find words or phrases for phone numbers
  1879. X
  1880. X
  1881. X SYNOPSIS
  1882. X      phonewrd [-n numerals allowed] [-s single letter words] [-q q mapping]
  1883. X      [-z z mapping] [-d dictionary path] [-l word length] [-m numeral map]
  1884. X      [-v] [-r] [-c] [-C] phone_number1[*...] [phone_number2[*...] ...]
  1885. X
  1886. X
  1887. X DESCRIPTION
  1888. X      phonewrd finds words or phrases which match the input phone numbers.
  1889. X      It uses the dictionary word list /usr/dict/words by default, ignoring
  1890. X      one letter words (see -s).  There are a wide variety of options
  1891. X      available to allow various sorts of pattern matching.  Words with
  1892. X      apostrophes or other punctuation are acceptable.    If more than one
  1893. X      phone number is given, each number and its combinations are output.
  1894. X
  1895. X      If the input phone number ends with one or more *, e.g. 555-1212**,
  1896. X      then the output phrases can be up to that many characters longer that
  1897. X      the input number.  This usually yields a few extra phrases, and
  1898. X      dialing those extra digits at the end normally doesn't cause problems.
  1899. X
  1900. X      The input phone number can have letters in it, in which case that
  1901. X      digit is forced to be that letter in any phrase.    This can help pare
  1902. X      down a long output list for a number.
  1903. X
  1904. X      If the number is entirely letters, it is converted to numbers and
  1905. X      output.
  1906. X
  1907. X      If you don't have a dictionary on your system, a free set of
  1908. X      dictionary files are available via FTP from prep.ai.mit.edu in
  1909. X      /pub/gnu/ispell*.tar.z (you'll also need "gzip" in this same directory
  1910. X      to uncompress this archive).  phonewrd understands the "affix" format
  1911. X      used for the dictionaries in ispell and expands words accordingly.
  1912. X
  1913. X      If you can't get your hands on any dictionary files, look at the -r,
  1914. X      -c, and (for the totally insane) -C options.  These options do not
  1915. X      need a dictionary (and so will do either very little or too much).
  1916. X
  1917. X
  1918. X Options
  1919. X      -n numerals allowed
  1920. X       This option allows phone digits to be replaced by numerals, up to
  1921. X       the limit set.  For example -n 2 means that any two digits of the
  1922. X       phone number can be left as digits and not made a part of any
  1923. X       word.  This can help find phrases for phone numbers with poor
  1924. X       letter combinations.  Note that numerals that have no
  1925. X       corresponding letters (i.e. 0 and 1 by default) do not count
  1926. X       against this total, nor do one letter words set using -s.
  1927. X       Default setting is 0.
  1928. X
  1929. X
  1930. X
  1931. X                    - 1 -       Formatted:  June 23, 1993
  1932. X
  1933. X
  1934. X
  1935. X
  1936. X
  1937. X
  1938. X PHONEWRD(1)                             PHONEWRD(1)
  1939. X
  1940. X
  1941. X
  1942. X
  1943. X      -s single letter words
  1944. X       This option gives all allowed single letter words (including
  1945. X       numeral "words", e.g. "I love U 2").  Normally dictionaries have
  1946. X       all letters of the alphabet as being legal words, which gives a
  1947. X       ton of useless phrases.  By default the only allowed single
  1948. X       letter words are "a", "i", and "o".    As an example of how to
  1949. X       change this, -s abcdgimoptuxy248 means that any of these single
  1950. X       letters are now considered words.  This can help find phrases for
  1951. X       phone numbers with awful letter combinations and in general open
  1952. X       up more possibilities.  To have no single letter words at all,
  1953. X       use -s =.
  1954. X
  1955. X
  1956. X      -q mapping
  1957. X       This option maps the letter q to the given numeral.    For example,
  1958. X       -q 0 means that phone numbers with 0's in them can have q's
  1959. X       substituted for the 0's.  This usually doesn't help much anyway,
  1960. X       and it's up to you to remember that q == 0, but there you go.  By
  1961. X       default q is not mapped.
  1962. X
  1963. X
  1964. X      -z mapping
  1965. X       Amazingly similar to -q.  This option maps the letter z to the
  1966. X       given numeral.  For example, -z 0 means that phone numbers with
  1967. X       0's in them can have z's substituted for the 0's.  This usually
  1968. X       doesn't help much anyway, and it's up to you to remember that z
  1969. X       == 0, but there it is.  By default z is not mapped.
  1970. X
  1971. X
  1972. X      -d dictionary path
  1973. X       This option allows alternate dictionaries to be used.  For
  1974. X       example,
  1975. X        -d /users/fred/dict/star_trek_words
  1976. X       will use (only) Fred's geek-speak dictionary.  Default is
  1977. X       /usr/dict/words (or whatever it is on your system), which is not
  1978. X       used if any dictionary path is specified.
  1979. X
  1980. X       Multiple dictionaries can be used by specifying multiple paths.
  1981. X       For example, to use the geek speak dictionary along with the
  1982. X       regular system dictionary, do:
  1983. X        -d /users/fred/dict/star_trek_words -d /usr/dict/words
  1984. X       A path of "." means use the default dictionary, so quicker yet
  1985. X       is:
  1986. X        -d . -d /users/fred/dict/star_trek_words
  1987. X       In this way additional dictionaries can either replace or be
  1988. X       added to the default.
  1989. X
  1990. X       Note that phonewrd understands the "affix" format used for the
  1991. X       dictionaries of GNU's ispell and expands words accordingly, so
  1992. X       these can be used to add more words to the search.
  1993. X
  1994. X
  1995. X
  1996. X
  1997. X                    - 2 -       Formatted:  June 23, 1993
  1998. X
  1999. X
  2000. X
  2001. X
  2002. X
  2003. X
  2004. X PHONEWRD(1)                             PHONEWRD(1)
  2005. X
  2006. X
  2007. X
  2008. X
  2009. X      -l word length
  2010. X       This option allows a minimum word length to be set.    For example,
  2011. X       -l 3 means no words shorter than 3 letters are used for building
  2012. X       phrases.  User defined single letter words (see -s) are not
  2013. X       affected by this option.  Single letter words from the dictionary
  2014. X       are ignored no matter what the setting (since most dictionaries
  2015. X       include exciting words like "h", "w" and so on).  Default is 1.
  2016. X
  2017. X
  2018. X      -m numeral map
  2019. X       For those of you from a different planet, this will remap the
  2020. X       letters to different numerals.  For example,
  2021. X        -m 2223334445556667.77888999.
  2022. X       is the default setting, a normal telephone.    The "." (or any
  2023. X       other non-numeric value) means don't map the letter to any key.
  2024. X       You get a banana for using -v with this command.
  2025. X
  2026. X
  2027. X      -v   Verbose option:  words fitting into the various number slots are
  2028. X       output and other warnings and whatnot are all output to stderr.
  2029. X       This can help you "roll your own" if the program does not find
  2030. X       anything to your liking.
  2031. X
  2032. X
  2033. X      -r   This option is for those without a dictionary.  It simply outputs
  2034. X       the letters possible for each digit in a column so that you can
  2035. X       search for combinations more easily.  No dictionary search is
  2036. X       done, so none is needed.
  2037. X
  2038. X
  2039. X      -c   This option is also for those without a dictionary.    It generates
  2040. X       all permutations of letters and numerals valid for the input
  2041. X       phone number and outputs these one per line.  If you do:
  2042. X
  2043. X        phonewrd -c 234-5678 > junk1
  2044. X        spell junk1 > junk2
  2045. X        diff junk1 junk2 | grep "<"
  2046. X
  2047. X       you'll get any 7 letter words formed which pass the spell
  2048. X       checker.  It's a long shot, but you never know...
  2049. X
  2050. X
  2051. X      -C   This option is also for those without a dictionary and too much
  2052. X       disk space and CPU time.  It generates all permutations of
  2053. X       letters and numerals valid for the input phone number and outputs
  2054. X       these one per line, plus it makes all permutations of spacing for
  2055. X       each of these permutations.    3^7 * 2^6 = 139968 phrases for a
  2056. X       phone number without 0's and 1's.  Then send each one through
  2057. X       your spell checker and if any errors are found, discard it and go
  2058. X       to the next.  It will take awhile, but this is the price you pay
  2059. X       for not having any dictionary available.
  2060. X
  2061. X
  2062. X
  2063. X                    - 3 -       Formatted:  June 23, 1993
  2064. X
  2065. X
  2066. X
  2067. X
  2068. X
  2069. X
  2070. X PHONEWRD(1)                             PHONEWRD(1)
  2071. X
  2072. X
  2073. X
  2074. X
  2075. X EXAMPLES
  2076. X      phonewrd 542-5968 gives the words:
  2077. X      kick you
  2078. X      lick you
  2079. X
  2080. X      phonewrd 542-5968 -n 1
  2081. X      allows more slack, letting one character be a numeral:
  2082. X      kick you
  2083. X      lick you
  2084. X      5 gal you
  2085. X      5 Hal you
  2086. X      which gives a little wider range of possibilities.
  2087. X
  2088. X      phonewrd 5683968 -l 3
  2089. X      gives words 3 characters or longer:
  2090. X      Jove you
  2091. X      loud you
  2092. X      love you
  2093. X
  2094. X      phonewrd 486-9364 -s aio248
  2095. X      gives many combinations, but the -s aio248 allows the extra
  2096. X      combination
  2097. X      I 8 my dog
  2098. X
  2099. X      phonewrd 787-3648*
  2100. X      allows the extra combination
  2101. X      strength
  2102. X      as one * at the end means up to 1 additional character can be output
  2103. X      at the end of the phrase.
  2104. X
  2105. X      phonewrd 861-2076
  2106. X      doesn't yield much of anything, so setting 0 to o and 1 to i:
  2107. X      phonewrd 86i-2o76
  2108. X      we get
  2109. X      unicorn
  2110. X      so un1c0rn fits the original phone number.  Putting a letter in a
  2111. X      phone number forces the use of that letter in that position.
  2112. X
  2113. X      phonewrd -r 266-7883
  2114. X      yields
  2115. X        a  m  m  p    t  t  d
  2116. X        b  n  n  r    u  u  e
  2117. X        c  o  o  s    v  v  f
  2118. X      The letters for each numeral are output in columns and so can be more
  2119. X      easily searched by hand for interesting phrases.    Yes, I know you
  2120. X      found bon stud in there, but what seven letter word can be found?  If
  2121. X      you find it, pat yourself on the cranium for having a brain and being
  2122. X      able to quickly cull through the 2187 possibilities.
  2123. X
  2124. X      phonewrd -c 2667883
  2125. X      will list all the possible combinations, e.g.
  2126. X
  2127. X
  2128. X
  2129. X                    - 4 -       Formatted:  June 23, 1993
  2130. X
  2131. X
  2132. X
  2133. X
  2134. X
  2135. X
  2136. X PHONEWRD(1)                             PHONEWRD(1)
  2137. X
  2138. X
  2139. X
  2140. X
  2141. X      ammpttd
  2142. X      ammptte
  2143. X      ammpttf
  2144. X      ammptud
  2145. X      ammptue
  2146. X      ...
  2147. X      which can then be run through a spell checker and so searched for
  2148. X      seven letter words (see the -c option for how to do this).
  2149. X
  2150. X      phonewrd 234-5678 -v
  2151. X      will output all words which fit into the various places in the phone
  2152. X      number.  For example, one output entry is:
  2153. X      Digit 5, length 3:
  2154. X       opt
  2155. X      which means that for the fifth digit of the number, all words of
  2156. X      length three which fit (i.e. translate into "678" in this case) are
  2157. X      displayed.
  2158. X
  2159. X
  2160. X TO BE DONE
  2161. X      It would be cute to be able to map numerals to strings, e.g. 460-2253
  2162. X      with "0" mapped to "OPER" gives "inOPERable", or 434-4637 with "4"
  2163. X      mapped to "for" gives "FOReigner".
  2164. X
  2165. X
  2166. X AUTHOR
  2167. X      Eric Haines, erich@eye.com
  2168. X
  2169. X
  2170. X
  2171. X
  2172. X
  2173. X
  2174. X
  2175. X
  2176. X
  2177. X
  2178. X
  2179. X
  2180. X
  2181. X
  2182. X
  2183. X
  2184. X
  2185. X
  2186. X
  2187. X
  2188. X
  2189. X
  2190. X
  2191. X
  2192. X
  2193. X
  2194. X
  2195. X                    - 5 -       Formatted:  June 23, 1993
  2196. X
  2197. X
  2198. X
  2199. END_OF_FILE
  2200.   if test 9737 -ne `wc -c <'phonewrd.txt'`; then
  2201.     echo shar: \"'phonewrd.txt'\" unpacked with wrong size!
  2202.   fi
  2203.   # end of 'phonewrd.txt'
  2204. fi
  2205. echo shar: End of archive 1 \(of 1\).
  2206. cp /dev/null ark1isdone
  2207. MISSING=""
  2208. for I in 1 ; do
  2209.     if test ! -f ark${I}isdone ; then
  2210.     MISSING="${MISSING} ${I}"
  2211.     fi
  2212. done
  2213. if test "${MISSING}" = "" ; then
  2214.     echo You have the archive.
  2215.     rm -f ark[1-9]isdone
  2216. else
  2217.     echo You still must unpack the following archives:
  2218.     echo "        " ${MISSING}
  2219. fi
  2220. exit 0
  2221. exit 0 # Just in case...
  2222.