home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #26 / NN_1992_26.iso / spool / sci / crypt / 4632 < prev    next >
Encoding:
Internet Message Format  |  1992-11-11  |  36.6 KB

  1. Path: sparky!uunet!charon.amdahl.com!pacbell.com!sgiblab!sdd.hp.com!elroy.jpl.nasa.gov!ames!agate!stanford.edu!rutgers!igor.rutgers.edu!cadenza.rutgers.edu!masticol
  2. From: masticol@cadenza.rutgers.edu (Steve Masticola)
  3. Newsgroups: sci.crypt
  4. Subject: Re: Anagram generator
  5. Keywords: huge berserk rebel warthog
  6. Message-ID: <Nov.11.20.16.21.1992.20673@cadenza.rutgers.edu>
  7. Date: 12 Nov 92 01:16:22 GMT
  8. References: <Nov.11.19.44.45.1992.20296@cadenza.rutgers.edu>
  9. Organization: Rutgers Univ., New Brunswick, N.J.
  10. Lines: 1192
  11.  
  12. Never mind; I found one on the games archive. I'm posting it below for
  13. people's amazement.
  14.  
  15. - Stephen Price "parenthesis complicate" Masticola.
  16.  
  17. ----8<----8<----8<----8<----8<----8<----8<----8<----8<----8<----8<----
  18. #! /bin/sh
  19. # This is a shell archive.  Remove anything before this line, then unpack
  20. # it by saving it into a file and typing "sh file".  To overwrite existing
  21. # files, type "sh file -c".  You can also feed this as standard input via
  22. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  23. # will see the following message at the end:
  24. #        "End of shell archive."
  25. # Contents:  README HINTS a386gram.asm anagram.1l anagram.c unproto.pl
  26. # Wrapped by raymond@sunkist.berkeley.edu on Sun Aug 18 22:05:50 1991
  27. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  28. if test -f 'README' -a "${1}" != "-c" ; then 
  29.   echo shar: Will not clobber existing file \"'README'\"
  30. else
  31. echo shar: Extracting \"'README'\" \(1325 characters\)
  32. sed "s/^X//" >'README' <<'END_OF_FILE'
  33. XBefore installing, make sure you have...
  34. X
  35. X    1.  The anagram.c source code.
  36. X    2.  The manual page (anagram.1l)
  37. X    3.  A word list.  (/usr/dict/words is a good start.)
  38. X
  39. XEither...
  40. X
  41. X    3a. A compiler that understands ANSI-style prototypes.
  42. X
  43. Xor
  44. X
  45. X    3b.1. A K&R-style compiler.
  46. X    3b.2. Larry Wall's `perl' program.
  47. X
  48. XInstallation procedure:
  49. X
  50. X    0.  If your compiler does not understand prototypes, filter
  51. X        the program through the unproto.pl perl script.
  52. X
  53. X    1.  Go into anagram.c and search for the phrase `Before compiling'.
  54. X        Read and understand that paragraph.
  55. X
  56. X    2.  Compile the program, using the hints in the HINTS file if
  57. X        necessary.
  58. X
  59. X    3.  Optionally install the man page and the executable someplace.
  60. X
  61. X    4.  Enjoy.
  62. X
  63. XHISTORY
  64. X
  65. X    The program was written because I wanted an anagram program.
  66. X    The existing anagram program in comp.sources.games is, by
  67. X    its own admission, `Extremely inefficient'.
  68. X
  69. X    I thought the bit-field idea was due to some article in the CACM
  70. X    that I read many years ago, but now that I've gone back to look
  71. X    for it, it isn't there.  So I don't know where I got it from.
  72. X
  73. X    Acknowledgements to Brian Scearce, for sending me a copy of his
  74. X    anagram program, which I studied before writing this one.
  75. X
  76. XAUTHOR
  77. X
  78. X    Raymond Chen (rjc@math.princeton.edu)
  79. END_OF_FILE
  80. if test 1325 -ne `wc -c <'README'`; then
  81.     echo shar: \"'README'\" unpacked with wrong size!
  82. fi
  83. # end of 'README'
  84. fi
  85. if test -f 'HINTS' -a "${1}" != "-c" ; then 
  86.   echo shar: Will not clobber existing file \"'HINTS'\"
  87. else
  88. echo shar: Extracting \"'HINTS'\" \(1143 characters\)
  89. sed "s/^X//" >'HINTS' <<'END_OF_FILE'
  90. XHints for compiling on various platforms
  91. X
  92. X-------------------------------------------------------------------------------
  93. XUNIX with a K&R compiler
  94. X
  95. X    perl unproto.pl anagram.c >anagramkr.c
  96. X    cc -o anagram anagramkr.c
  97. X
  98. X-------------------------------------------------------------------------------
  99. XUNIX with an ANSIish compiler
  100. X
  101. X    cc -o anagram anagram.c
  102. X
  103. X-------------------------------------------------------------------------------
  104. XIBM PC with Turbo C, no 80386 chip
  105. X
  106. X    tcc -ms anagram.c
  107. Xor  tcc -mc anagram.c           (for anagramming larger phrases)
  108. X
  109. X-------------------------------------------------------------------------------
  110. XIBM PC with Turbo C and 80386 chip
  111. X
  112. X    masm /mx a386gram;
  113. X    tcc -ms -DASM_ANAGRAM anagram.c a386gram.obj
  114. X-------------------------------------------------------------------------------
  115. XIBM PC with MSC, no 80386 chip
  116. X
  117. X    cl -AS -Ox anagram.c
  118. Xor  cl -AC -Ox anagram.c        (for anagramming larger phrases)
  119. X-------------------------------------------------------------------------------
  120. XIBM PC with MSC and 80386 chip
  121. X
  122. X    masm /mx a386gram;
  123. X    cl -AS -Ox -DASM_ANAGRAM anagram.c a386gram.obj
  124. END_OF_FILE
  125. if test 1143 -ne `wc -c <'HINTS'`; then
  126.     echo shar: \"'HINTS'\" unpacked with wrong size!
  127. fi
  128. # end of 'HINTS'
  129. fi
  130. if test -f 'a386gram.asm' -a "${1}" != "-c" ; then 
  131.   echo shar: Will not clobber existing file \"'a386gram.asm'\"
  132. else
  133. echo shar: Extracting \"'a386gram.asm'\" \(4526 characters\)
  134. sed "s/^X//" >'a386gram.asm' <<'END_OF_FILE'
  135. X_TEXT    segment    byte public 'CODE'
  136. XDGROUP    group    _DATA,_BSS
  137. X    assume    cs:_TEXT,ds:DGROUP,ss:DGROUP
  138. X    extrn    _kbhit:near
  139. X    extrn    _longjmp:near
  140. X    extrn    _DumpWords:near
  141. X_TEXT   ends
  142. X
  143. X_DATA    segment word public 'DATA'
  144. X_DATA   ends
  145. X
  146. X_BSS    segment word public 'BSS'
  147. X_BSS    ends
  148. X
  149. X    extrn    _apwCand:word
  150. X    extrn    _cpwCand:word
  151. X    extrn    _apwSol:word
  152. X    extrn    _cpwLast:word
  153. X    extrn    _cchPhraseLength:word
  154. X    extrn    _jbAnagram:word
  155. X    extrn    _achByFrequency:byte
  156. X    extrn    _alPhrase:word
  157. X    extrn    _aqMainSign:dword
  158. X
  159. X_TEXT    segment byte public 'CODE'
  160. X        .386
  161. X_FindAnagram    proc    near
  162. X    push    bp
  163. X    movzx    ebp,sp            ; extend to 32 bits
  164. X    sub    sp,16
  165. X    push    si
  166. X    push    di
  167. X
  168. X;  PPWord ppwEnd = &apwCand[cpwCand];
  169. X    mov    ax,word ptr DGROUP:_cpwCand
  170. X    shl    ax,1
  171. X    add    ax,offset DGROUP:_apwCand
  172. X    mov    word ptr [bp-2],ax
  173. X
  174. X; if (HaltProcessing) longjmp(jbAnagram, 1);
  175. X    call    near ptr _kbhit
  176. X    or    ax,ax
  177. X    je    @nointerrupt
  178. X
  179. X    push    1
  180. X    push    offset DGROUP:_jbAnagram
  181. X    call    near ptr _longjmp
  182. X;    the call never returns
  183. X
  184. X; during the letter search, we enregister...
  185. X;   eax = qMask
  186. X;    bx = iq
  187. X;    si = &alPhrase[achByFrequency[iLetter]]
  188. X;    di = iLetter
  189. X;    cx = used during shifting
  190. X
  191. X@nointerrupt:
  192. X    movzx    edi, word ptr [bp+8]    ; iLetter
  193. X    dec    di            ; will be incremented later
  194. X    xor    ebx, ebx        ; top bytes of ebx stay clear
  195. X    xor    edx, edx        ; top bytes of edx stay clear
  196. X
  197. X@nextletter:
  198. X    inc    di
  199. X
  200. X; iq = alPhrase[achByFrequency[iLetter]].iq;
  201. X    mov    dl,byte ptr DGROUP:_achByFrequency[edi] ; edx = achByFreq[cx]
  202. X    lea    esi, _alPhrase[8*edx]    ; esi = &alPhrase[edx]
  203. X
  204. X    mov    bx,[si+6]        ; bx = .iq
  205. X
  206. X; qMask = alPhrase[achByFrequency[iLetter]].uBits <<
  207. X;      alPhrase[achByFrequency[iLetter]].uShift;
  208. X
  209. X    movzx    eax, word ptr [si+4]    ; .uBits
  210. X    mov    cl, [si+2]        ; .uShift
  211. X    shl    eax,cl
  212. X
  213. X;    if (pqMask[iq] & qMask) break;
  214. X    movzx    ecx, word ptr [bp+4]    ; pqMask
  215. X    test    eax, [ecx+ebx*4]    ;   qMask
  216. X    je    @nextletter
  217. X
  218. X; de-register the register variables
  219. X    mov    [bp+8], di        ; iLetter
  220. X    mov    word ptr [bp-4], bx    ; iq
  221. X    mov    dword ptr [bp-8], eax    ; qMask
  222. X
  223. X; enregister edi
  224. X    movzx    edi, word ptr [bp+6]    ; ppwStart
  225. X    sub    di, 2            ; will be incremented later
  226. X
  227. X@enregister:
  228. X; enregister other cool things.  Clear top word of esi and ebx.
  229. X;    eax = scratch
  230. X;    ebx  = ppwEnd
  231. X;    ecx = pqMask[0]
  232. X;    edx = aqMainSign[0]
  233. X;    esi = pw
  234. X;    edi = ppwStart
  235. X
  236. X    movzx    ebx, word ptr [bp+4]    ; pqMask
  237. X    mov    ecx, dword ptr [ebx]    ; pqMask[0]
  238. X    mov    edx, dword ptr DGROUP:_aqMainSign ; aqMainSign
  239. X    movzx    ebx, word ptr [bp-2]    ; ppwEnd
  240. X    xor    esi, esi        ; clear top
  241. X
  242. X@inc_cont:                ; increment and continue
  243. X    inc    di            ; ppwStart++;
  244. X    inc    di
  245. X
  246. X@cont:                    ; continue, no increment
  247. X    cmp    di,bx            ; while (ppwStart < ppwEnd) ...
  248. X    jae    @endloop
  249. X
  250. X    mov    si,[di]         ; pw = *ppwStart
  251. X
  252. X;    if ((aqNext[0] = pqMask[0] - pw->aqMask[0]) & aqMainSign[0]) goto fail;
  253. X    mov    eax, ecx        ; eax = pqMask[0]
  254. X    sub    eax, [esi]        ;          - pw->aqMask[0]
  255. X    test    eax, edx        ;        & aqMainSign
  256. X    jne    @inc_cont
  257. X    mov    [bp-16], eax        ; store into aqNext[0]
  258. X
  259. X;    if ((aqNext[1] = pqMask[1] - pw->aqMask[1]) & aqMainSign[1]) goto fail;
  260. X    movzx    eax, word ptr [bp+4]    ; pqMask
  261. X    mov    eax, [eax+4]        ; eax = pqMask[1]
  262. X    sub    eax, [esi+4]        ;          - pw->aqMask[1]
  263. X    test    eax, dword ptr DGROUP:_aqMainSign+4 ; & aqMainSign[1]
  264. X    jne    @inc_cont
  265. X    mov    [bp-12], eax        ; store into aqNext[1]
  266. X
  267. X;    if ((pw->aqMask[iq] & qMask) == 0)
  268. X    movzx    eax, word ptr [bp-4]    ; iq
  269. X    mov    eax, [esi+4*eax]    ; pw->aqMask[iq]
  270. X    and    eax, [bp-8]        ; qMask
  271. X    jne    @try_it
  272. X
  273. X; *ppwStart = *--ppwEnd; *ppwEnd = pw; continue;
  274. X    dec    bx
  275. X    dec    bx            ; --ppwEnd
  276. X    xchg    si,word ptr [bx]    ; *ppwEnd = pw;
  277. X    mov    word ptr [di],si    ; *ppwStart = previous value of *ppwEnd
  278. X    jmp    short @cont
  279. X
  280. X@try_it:
  281. X;    ALL REGISTER VALUES ARE NOW BOGUS.
  282. X;    the only ones that must be preserved are edi and esi.
  283. X
  284. X; apwSol[cpwLast++] = pw;
  285. X    mov    bx,word ptr DGROUP:_cpwLast
  286. X    shl    bx,1
  287. X    mov    word ptr DGROUP:_apwSol[bx],si    ;pw
  288. X    inc    word ptr DGROUP:_cpwLast
  289. X
  290. X; if (cchPhraseLength -= pw->cchLength) {
  291. X    mov    ax,word ptr [si+12]
  292. X    sub    word ptr DGROUP:_cchPhraseLength,ax
  293. X    je    @found_anagram
  294. X
  295. X; ppwEnd = &apwCand[cpwCand];
  296. X    mov    ax,word ptr DGROUP:_cpwCand
  297. X    shl    ax,1
  298. X    add    ax,offset DGROUP:_apwCand
  299. X    mov    word ptr [bp-2],ax
  300. X
  301. X; FindAnagram(aqNext, ppwStart, iLetter);
  302. X    push    word ptr [bp+8]
  303. X    push    di
  304. X    lea    ax,word ptr [bp-16]
  305. X    push    ax
  306. X    call    near ptr _FindAnagram
  307. X    add    sp,6
  308. X
  309. X; }
  310. X    jmp    short @resume_search
  311. X
  312. X; else DumpWords()
  313. X@found_anagram:
  314. X    call    near ptr _DumpWords
  315. X
  316. X; cchPhraseLength += pw->cchLength;
  317. X@resume_search:
  318. X    mov    ax,word ptr [si+12]
  319. X    add    word ptr DGROUP:_cchPhraseLength,ax
  320. X
  321. X; --cpwLast;
  322. X    dec    word ptr DGROUP:_cpwLast
  323. X
  324. X; }
  325. X    jmp    @enregister
  326. X
  327. X@endloop:
  328. X    pop    di
  329. X    pop    si
  330. X    leave
  331. X    ret
  332. X_FindAnagram    endp
  333. X
  334. X        public  _FindAnagram
  335. X
  336. X_TEXT    ends
  337. X
  338. X        end
  339. END_OF_FILE
  340. if test 4526 -ne `wc -c <'a386gram.asm'`; then
  341.     echo shar: \"'a386gram.asm'\" unpacked with wrong size!
  342. fi
  343. # end of 'a386gram.asm'
  344. fi
  345. if test -f 'anagram.1l' -a "${1}" != "-c" ; then 
  346.   echo shar: Will not clobber existing file \"'anagram.1l'\"
  347. else
  348. echo shar: Extracting \"'anagram.1l'\" \(2367 characters\)
  349. sed "s/^X//" >'anagram.1l' <<'END_OF_FILE'
  350. X.TH ANAGRAM 1
  351. X.SH NAME
  352. Xanagram \- anagram words and phrases
  353. X.SH SYNOPSIS
  354. X.B anagram
  355. Xdictionary [ minlength ]
  356. X.SH DESCRIPTION
  357. X.I anagram
  358. Xreads phrases from
  359. X.I stdin
  360. Xand attempts to find anagrams for the phrase using `words' in the specified
  361. X.IR dictionary .
  362. XIn the discussion to follow, a `word' is a string of letters that are
  363. Xtreated as as a unit for the purpose of anagramming.  A `word'
  364. Xcan contain punctuation marks and embedded spaces.  For example,
  365. Xthe three-word phrase `cul de sac' appears as a single `word' in
  366. Xmy dictionary.
  367. X.PP
  368. XThe dictionary file must consist of a sequence of lines, one `word' per
  369. Xline.
  370. XDuplicate `words' are not weeded out, and lines with more than one word
  371. Xare treated as multi-word `words'.
  372. X.PP
  373. XOnly `words' with at least
  374. X.I minlength
  375. Xletters are considered.
  376. X(Default value is 3.)
  377. X.PP
  378. XIf a phrase begins with a digit, it is interpreted to specify a new
  379. Xvalue of
  380. X.IR minlength ,
  381. Xrather than a phrase to be anagrammed.
  382. XAnd if a phrase begins with a question-mark, then the list of candidates
  383. Xfor the most recently anagrammed phrase is printed.
  384. X.SH DIAGNOSTICS
  385. X.TP
  386. XCannot stat dictionary / Cannot open dictionary
  387. XThe
  388. X.I dictionary
  389. Xfile specified on the command line could not be accessed.
  390. XCheck that the file exists and has the appropriate permissions.
  391. X.TP
  392. XDictionary too large; increase MAXWORDS
  393. XThe indicated dictionary file contains more than MAXWORDS `words'.
  394. XThe value of MAXWORDS is set at compile-time.  The supplied value
  395. Xof 26000 is large enough to handle
  396. X.IR /usr/dict/words .
  397. X.TP
  398. XMAX_QUADS not large enough
  399. XYou attempted to anagram a phrase that has too many letters.
  400. XYou'll need to increase the value of MAX_QUADS and recompile.
  401. X.TP
  402. XToo many candidates
  403. XThere were more than MAXCAND `words' that fit into the phrase.
  404. XIncrease the compile-time constant MAXCAND and recompile.
  405. X.SH BUGS
  406. XThe `?' command does not actually list all of the `words' that fit
  407. Xinside the phrase.  Some words are pruned out early in the
  408. Xselection process because their use would leave fewer than
  409. X.I minlength
  410. Xletters available in the rest of the phrase.
  411. X.SH ACKNOWLEDGEMENTS
  412. XThe author would like to thank Brian Scearce for allowing
  413. Xhis own anagram program to be studied in preparation for writing
  414. Xthis one.  No code for this program was taken from his program,
  415. Xbut much of the spirit shines through.
  416. X.SH AUTHOR
  417. XRaymond Chen (raymond@math.berkeley.edu)
  418. END_OF_FILE
  419. if test 2367 -ne `wc -c <'anagram.1l'`; then
  420.     echo shar: \"'anagram.1l'\" unpacked with wrong size!
  421. fi
  422. # end of 'anagram.1l'
  423. fi
  424. if test -f 'anagram.c' -a "${1}" != "-c" ; then 
  425.   echo shar: Will not clobber existing file \"'anagram.c'\"
  426. else
  427. echo shar: Extracting \"'anagram.c'\" \(20332 characters\)
  428. sed "s/^X//" >'anagram.c' <<'END_OF_FILE'
  429. X/*
  430. X *  Anagram program by Raymond Chen,
  431. X *  inspired by a similar program by Brian Scearce
  432. X *
  433. X *  This program is Copyright 1991 by Raymond Chen.
  434. X *              (rjc@math.princeton.edu)
  435. X *
  436. X *  This program may be freely distributed provided all alterations
  437. X *  to the original are clearly indicated as such.
  438. X */
  439. X
  440. X/* There are two tricks.  First is the Basic Idea:
  441. X *
  442. X * When the user types in a phrase, the phrase is first preprocessed to
  443. X * determine how many of each letter appears.  A bit field is then constructed
  444. X * dynamically, such that each field is large enough to hold the next power
  445. X * of two larger than the number of times the character appears.  For example,
  446. X * if the phrase is `hello, world', the bit field would be
  447. X *
  448. X *   00 00 00 000 000 00 00
  449. X *    d e   h   l   o  r  w
  450. X *
  451. X * The phrase `hello, world', itself would be encoded as
  452. X *
  453. X *   01 01 01 011 010 01 01
  454. X *    d e   h   l   o  r  w
  455. X *
  456. X * and the word `hollow' would be encoded as
  457. X *
  458. X *   00 00 01 010 010 00 01
  459. X *    d  e  h   l   o  r  w
  460. X *
  461. X * The top bit of each field is set in a special value called the `sign'.
  462. X * Here, the sign would be
  463. X *
  464. X *   10 10 10 100 100 10 10
  465. X *    d  e  h   l   o  r  w
  466. X *
  467. X * The reason for packing the values into a bit field is that the operation
  468. X * of subtracting out the letters of a word from the current phrase can be
  469. X * carried out in parallel.  for example, subtracting the word `hello' from
  470. X * the phrase `hello, world', is merely
  471. X *
  472. X *    d e   h   l   o  r  w
  473. X *   01 01 01 011 010 01 01 (dehllloorw)
  474. X * - 00 00 01 010 010 00 01 (hlloow)
  475. X * ========================
  476. X *   01 01 00 001 000 01 00 (delr)
  477. X *
  478. X * Since none of the sign bits is set, the word fits, and we can continue.
  479. X * Suppose the next word we tried was `hood'.
  480. X *
  481. X *    d e   h   l   o  r  w
  482. X *   01 01 00 001 000 01 00 (delr)
  483. X * - 01 00 01 000 010 00 00 (hood)
  484. X * ========================
  485. X *   00 00 11 000 110 01 00
  486. X *         ^      ^
  487. X * A sign bit is set.  (Two, actually.)  This means that `hood' does not
  488. X * fit in `delr', so we skip it and try another word.  (Observe that
  489. X * when a sign bit becomes set, it screws up the values for the letters to
  490. X * the left of that bit, but that's not important.)
  491. X *
  492. X * The inner loop of an anagram program is testing to see if a
  493. X * word fits in the collection of untried letters.  Traditional methods
  494. X * keep an array of 26 integers, which are then compared in turn.  This
  495. X * means that there are 26 comparisons per word.
  496. X *
  497. X * This method reduces the number of comparisons to MAX_QUAD, typically 2.
  498. X * Instead of looping through an array, we merely perform the indicated
  499. X * subtraction and test if any of the sign bits is set.
  500. X */
  501. X
  502. X/* The nuts and bolts:
  503. X *
  504. X * The dictionary is loaded and preprocessed.  The preprocessed dictionary
  505. X * is a concatenation of copies of the structure:
  506. X *
  507. X * struct dictword {
  508. X *     char bStructureSize;             -- size of this structure
  509. X *     char cLetters;                   -- number of letters in the word
  510. X *     char achWord[];                  -- the word itself (0-terminated)
  511. X * }
  512. X *
  513. X * Since this is a variable-sized structure, we keep its size in the structure
  514. X * itself for rapid stepping through the table.
  515. X *
  516. X * When a phrase is typed in, it is first preprocessed as described in the
  517. X * Basic Idea.  We then go through the dictionary, testing each word.  If
  518. X * the word fits in our phrase, we build the bit field for its frequency
  519. X * table and add it to the list of candidates.
  520. X */
  521. X
  522. X/*
  523. X * The Second Trick:
  524. X *
  525. X * Before diving into our anagram search, we then tabulate how many times
  526. X * each letter appears in our list of candidates, and sort the table, with
  527. X * the rarest letter first.
  528. X *
  529. X * We then do our anagram search.
  530. X *
  531. X * Like most anagram programs, this program does a depth-first search.
  532. X * Although most anagram programs do some sort of heuristics to decide what
  533. X * order to place words in the list_of_candidates, the search itself proceeds
  534. X * according to a greedy algorithm.  That is, once you find a word that fits,
  535. X * subtract it and recurse.
  536. X *
  537. X * This anagram program exercises some restraint and does not march down
  538. X * every branch that shows itself.  Instead, it only goes down branches
  539. X * that use the rarest unused letter.  This helps to find dead ends faster.
  540. X *
  541. X * FindAnagram(unused_letters, list_of_candidates) {
  542. X *  l = the rarest letter as yet unused
  543. X *  For word in list_of_candidates {
  544. X *     if word does not fit in unused_letters, go on to the next word.
  545. X *     if word does not contain l, defer.
  546. X *      FindAnagram(unused_letters - word, list_of_candidates[word,...])
  547. X *  }
  548. X * }
  549. X *
  550. X *
  551. X * The heuristic of the Second Trick can probably be improved.  I invite
  552. X * anyone willing to improve it to do so.
  553. X */
  554. X
  555. X/* Use the accompanying `unproto' perl script to remove the ANSI-style
  556. X * prototypes, for those of you who have K&R compilers.
  557. X */
  558. X
  559. X#include <stdio.h>
  560. X#include <stdlib.h>
  561. X#include <ctype.h>
  562. X#include <sys/types.h>
  563. X#include <sys/stat.h>
  564. X#include <setjmp.h>
  565. X
  566. X/* Before compiling, make sure Quad and MASK_BITS are set properly.  For best
  567. X * results, make Quad the largest integer size supported on your machine.
  568. X * So if your machine has long longs, make Quad an unsigned long long.
  569. X * (I called it `Quad' because on most machines, the largest integer size
  570. X * supported is a four-byte unsigned long.)
  571. X *
  572. X * If you need to be able to anagram larger phrases, increase MAX_QUADS.
  573. X * If you increase it beyond 4, you'll have to add a few more loop unrolling
  574. X * steps to FindAnagram.
  575. X */
  576. X
  577. Xtypedef unsigned long Quad;             /* for building our bit mask */
  578. X#define MASK_BITS 32                    /* number of bits in a Quad */
  579. X
  580. X#define MAX_QUADS 2                     /* controls largest phrase */
  581. X
  582. X#define MAXWORDS 26000                  /* dictionary length */
  583. X#define MAXCAND  5000                   /* candidates */
  584. X#define MAXSOL   51                     /* words in the solution */
  585. X
  586. X#define ALPHABET 26                     /* letters in the alphabet */
  587. X#define ch2i(ch) ((ch)-'a')             /* convert letter to index */
  588. X#define i2ch(ch) ((ch)+'a')             /* convert index to letter */
  589. X
  590. X/* IBM PC's don't like globs of memory larger than 64K without
  591. X * special gyrations.  That's where the `huge's get stuck in.  And the
  592. X * two types of allocations on an IBM PC need to be handled differently.
  593. X *
  594. X * HaltProcessing is called during the anagram search.    If it returns nonzero,
  595. X * then the search is aborted.
  596. X *
  597. X * Cdecl is a macro expanded before the name of all functions that must
  598. X * use C-style parameter passing.  This lets you change the default parameter
  599. X * passing style for the other functions.
  600. X */
  601. X
  602. X#ifdef __MSDOS__
  603. X#   define MSDOS
  604. X#endif
  605. X
  606. X/* And finally, if you (1) are running an IBM PC with a 386 chip,
  607. X * (2) define the symbol ASM_ANAGRAM, (3) compile in the small memory model,
  608. X * (4) assemble the file A386GRAM.ASM separately, and (5) link A386GRAM.OBJ
  609. X * with this file, then you'll get a hand-optimized anagram finder that
  610. X * runs about twice as fast as the plain C version.
  611. X */
  612. X#ifdef MSDOS
  613. X#   define HaltProcessing()    kbhit()
  614. X#   define StringFormat        "%15Fs%c"
  615. X#   include <io.h>
  616. X#   include <conio.h>
  617. X#   ifdef __TURBOC__
  618. X#       include <alloc.h>
  619. X#       include <mem.h>
  620. X#       define bigmalloc       farmalloc
  621. X#       ifdef __SMALL__                     /* use sbrk instead of malloc */
  622. X#           define smallmalloc     sbrk
  623. X#           define smallmallocfail ((char *)-1)
  624. X#       else
  625. X#           define smallmalloc     malloc
  626. X#           define smallmallocfail (char*)0
  627. X#       endif
  628. X#       define Cdecl           cdecl
  629. X#   else
  630. X#   ifdef _MSC_VER
  631. X#       include <malloc.h>
  632. X#       define bigmalloc(n)    halloc(n,1)
  633. X#       define smallmalloc     malloc
  634. X#       define smallmallocfail (char*)0
  635. X#       define Cdecl           cdecl
  636. X#   else
  637. X            @@"Unknown IBM PC compiler type"@@
  638. X#   endif
  639. X#   endif
  640. X#else                                   /* UNIXish options */
  641. X    char *malloc();
  642. X#   define huge
  643. X#   define far
  644. X#   define StringFormat    "%15s%c"
  645. X#   define bigmalloc       malloc
  646. X#   define smallmalloc     malloc
  647. X#   define smallmallocfail (char *)0
  648. X#   define HaltProcessing() 0           /* no easy way to interrupt on UNIX */
  649. X#   define Cdecl
  650. X#endif
  651. X
  652. X/* Code to be used only when debugging lives inside Debug().
  653. X * Code to be used only when collecting statistics lives inside Stat().
  654. X */
  655. X#ifdef DEBUG
  656. X#define Debug(x) x
  657. X#else
  658. X#define Debug(x)
  659. X#endif
  660. X
  661. X#ifdef STAT
  662. X#define Stat(x) x
  663. X#else
  664. X#define Stat(x)
  665. X#endif
  666. X
  667. X/* A Word remembers the information about a candidate word. */
  668. Xtypedef struct {
  669. X    Quad aqMask[MAX_QUADS];             /* the word's mask */
  670. X    char huge *pchWord;                 /* the word itself */
  671. X    unsigned cchLength;                 /* letters in the word */
  672. X} Word, *PWord, **PPWord;
  673. X
  674. XPWord apwCand[MAXCAND];                 /* candidates we've found so far */
  675. Xunsigned cpwCand;                       /* how many of them? */
  676. X
  677. X/* A Letter remembers information about each letter in the phrase to be
  678. X * anagrammed.
  679. X */
  680. X
  681. Xtypedef struct {
  682. X    unsigned uFrequency;                /* how many times it appears */
  683. X    unsigned uShift;                    /* how to mask */
  684. X    unsigned uBits;                     /* the bit mask itself */
  685. X    unsigned iq;                        /* which Quad to inspect? */
  686. X} Letter, *PLetter;
  687. X
  688. XLetter alPhrase[ALPHABET];              /* statistics on the current phrase */
  689. X#define lPhrase(ch) alPhrase[ch2i(ch)]  /* quick access to a letter */
  690. X
  691. Xint cchPhraseLength;                    /* number of letters in phrase */
  692. X
  693. XQuad aqMainMask[MAX_QUADS];             /* the bit field for the full phrase */
  694. XQuad aqMainSign[MAX_QUADS];             /* where the sign bits are */
  695. X
  696. Xint cchMinLength = 3;
  697. X
  698. X/* auGlobalFrequency counts the number of times each letter appears, summed
  699. X * over all candidate words.  This is used to decide which letter to attack
  700. X * first.
  701. X */
  702. Xunsigned auGlobalFrequency[ALPHABET];
  703. Xchar achByFrequency[ALPHABET];          /* for sorting */
  704. X
  705. Xchar huge *pchDictionary;               /* the dictionary is read here */
  706. X
  707. X#define Zero(t) memset(t, 0, sizeof(t)) /* quickly zero out an integer array */
  708. X
  709. X/* Fatal -- print a message before expiring */
  710. Xvoid Fatal(char *pchMsg, unsigned u) {
  711. X    fprintf(stderr, pchMsg, u);
  712. X    exit(1);
  713. X}
  714. X
  715. X/* ReadDict -- read the dictionary file into memory and preprocess it
  716. X *
  717. X * A word of length cch in the dictionary is encoded as follows:
  718. X *
  719. X *    byte 0    = cch + 3
  720. X *    byte 1    = number of letters in the word
  721. X *    byte 2... = the word itself, null-terminated
  722. X *
  723. X * Observe that cch+3 is the length of the total encoding.  These
  724. X * byte streams are concatenated, and terminated with a 0.
  725. X */
  726. X
  727. Xvoid ReadDict(char *pchFile) {
  728. X    FILE *fp;
  729. X    char huge *pch;
  730. X    char huge *pchBase;
  731. X    unsigned long ulLen;
  732. X    unsigned cWords = 0;
  733. X    unsigned cLetters;
  734. X    int ch;
  735. X    struct stat statBuf;
  736. X
  737. X    if (stat(pchFile, &statBuf)) Fatal("Cannot stat dictionary\n", 0);
  738. X
  739. X    ulLen = statBuf.st_size + 2 * (unsigned long)MAXWORDS;
  740. X    pchBase = pchDictionary = bigmalloc(ulLen);
  741. X
  742. X    if(!pchDictionary) Fatal("Unable to allocate memory for dictionary\n", 0);
  743. X
  744. X    if ((fp = fopen(pchFile, "r")) == NULL) Fatal("Cannot open dictionary\n", 0);
  745. X
  746. X    while (!feof(fp)) {
  747. X        pch = pchBase+2;                /* reserve for length */
  748. X        cLetters = 0;
  749. X        while ((ch = fgetc(fp)) != '\n' && ch != EOF) {
  750. X            if (isalpha(ch)) cLetters++;
  751. X            *pch++ = ch;
  752. X        }
  753. X        *pch++ = '\0';
  754. X        *pchBase = pch - pchBase;
  755. X        pchBase[1] = cLetters;
  756. X        pchBase = pch;
  757. X        cWords++;
  758. X    }
  759. X    fclose(fp);
  760. X
  761. X    *pchBase++ = 0;
  762. X
  763. X    fprintf(stderr, "main dictionary has %u entries\n", cWords);
  764. X    if (cWords >= MAXWORDS) Fatal("Dictionary too large; increase MAXWORDS\n", 0);
  765. X    fprintf(stderr, "%lu bytes wasted\n", ulLen - (pchBase - pchDictionary));
  766. X}
  767. X
  768. Xvoid BuildMask(char *pchPhrase) {
  769. X    int i;
  770. X    int ch;
  771. X    unsigned iq;                        /* which Quad? */
  772. X    int cbtUsed;                        /* bits used in the current Quad */
  773. X    int cbtNeed;                        /* bits needed for current letter */
  774. X    Quad qNeed;                         /* used to build the mask */
  775. X
  776. X    Zero(alPhrase);
  777. X    Zero(aqMainMask);
  778. X    Zero(aqMainSign);
  779. X
  780. X    /* Tabulate letter frequencies in the phrase */
  781. X    cchPhraseLength = 0;
  782. X    while ((ch = *pchPhrase++) != '\0') {
  783. X        if (isalpha(ch)) {
  784. X            ch = tolower(ch);
  785. X            lPhrase(ch).uFrequency++;
  786. X            cchPhraseLength++;
  787. X        }
  788. X    }
  789. X
  790. X    /* Build shift masks */
  791. X    iq = 0;                             /* which quad being used */
  792. X    cbtUsed = 0;                        /* bits used so far */
  793. X
  794. X    for (i = 0; i < ALPHABET; i++) {
  795. X        if (alPhrase[i].uFrequency == 0) {
  796. X            auGlobalFrequency[i] = ~0;  /* to make it sort last */
  797. X        } else {
  798. X            auGlobalFrequency[i] = 0;
  799. X            for (cbtNeed = 1, qNeed = 1;
  800. X                 alPhrase[i].uFrequency >= qNeed;
  801. X                 cbtNeed++, qNeed <<= 1);
  802. X            if (cbtUsed + cbtNeed > MASK_BITS) {
  803. X                if (++iq >= MAX_QUADS) Fatal("MAX_QUADS not large enough\n", 0);
  804. X                cbtUsed = 0;
  805. X            }
  806. X            alPhrase[i].uBits = qNeed-1;
  807. X            if (cbtUsed) qNeed <<= cbtUsed;
  808. X            aqMainSign[iq] |= qNeed;
  809. X            aqMainMask[iq] |= (Quad)alPhrase[i].uFrequency << cbtUsed;
  810. X            alPhrase[i].uShift = cbtUsed;
  811. X            alPhrase[i].iq = iq;
  812. X            cbtUsed += cbtNeed;
  813. X        }
  814. X    }
  815. X}
  816. X
  817. XPWord NewWord(void) {
  818. X    PWord pw = smallmalloc(sizeof(Word));
  819. X    if ((char *)pw == smallmallocfail)
  820. X        Fatal("Out of memory after %d candidates\n", cpwCand);
  821. X    return pw;
  822. X}
  823. X
  824. X/* wprint -- print a word, followed by a space
  825. X *
  826. X * We would normally just use printf, but the string being printed is
  827. X * is a huge pointer (on an IBM PC), so special care must be taken.
  828. X */
  829. Xvoid wprint(char huge *pch) {
  830. X    while (*pch) putchar(*pch++);
  831. X    putchar(' ');
  832. X}
  833. X
  834. X/* NextWord -- get another candidate entry, creating if necessary */
  835. XPWord NextWord(void) {
  836. X    PWord pw;
  837. X    if (cpwCand >= MAXCAND) Fatal("Too many candidates\n", 0);
  838. X    if ((pw = apwCand[cpwCand++]) != 0) return pw;
  839. X    return apwCand[cpwCand-1] = NewWord();
  840. X}
  841. X
  842. X/* BuildWord -- build a Word structure from an ASCII word
  843. X * If the word does not fit, then do nothing.
  844. X */
  845. Xvoid BuildWord(char huge *pchWord) {
  846. X    unsigned char cchFrequency[ALPHABET];
  847. X    int i;
  848. X    char huge *pch = pchWord;
  849. X    PWord pw;
  850. X    int cchLength = 0;
  851. X
  852. X    Zero(cchFrequency);
  853. X    /* Build frequency table */
  854. X    while ((i = *pch++) != '\0') {
  855. X        if (!isalpha(i)) continue;
  856. X        i = ch2i(tolower(i));
  857. X        if (++cchFrequency[i] > alPhrase[i].uFrequency) return;
  858. X        ++cchLength;
  859. X    }
  860. X
  861. X    Debug(wprint(pchWord);)
  862. X
  863. X    /* Update global count */
  864. X    for (i = 0; i < ALPHABET; i++)
  865. X        auGlobalFrequency[i] += cchFrequency[i];
  866. X
  867. X    /* Create a Word structure and fill it in, including building the
  868. X     * bitfield of frequencies.
  869. X     */
  870. X    pw = NextWord();
  871. X    Zero(pw->aqMask);
  872. X    pw->pchWord = pchWord;
  873. X    pw->cchLength = cchLength;
  874. X    for (i = 0; i < ALPHABET; i++) {
  875. X        pw->aqMask[alPhrase[i].iq] |=
  876. X            (Quad)cchFrequency[i] << alPhrase[i].uShift;
  877. X    }
  878. X}
  879. X
  880. X/* AddWords -- build the list of candidates */
  881. Xvoid AddWords(void) {
  882. X    char huge *pch = pchDictionary;     /* walk through the dictionary */
  883. X
  884. X    cpwCand = 0;
  885. X
  886. X    while (*pch) {
  887. X        if ((pch[1] >= cchMinLength && pch[1] + cchMinLength <= cchPhraseLength)
  888. X            || pch[1] == cchPhraseLength) BuildWord(pch+2);
  889. X        pch += *pch;
  890. X    }
  891. X
  892. X    fprintf(stderr, "%d candidates\n", cpwCand);
  893. X}
  894. X
  895. Xvoid DumpCandidates(void) {
  896. X    unsigned u;
  897. X
  898. X    for (u = 0; u < cpwCand; u++)
  899. X        printf(StringFormat, apwCand[u]->pchWord, (u % 4 == 3) ? '\n' : ' ');
  900. X    printf("\n");
  901. X}
  902. X
  903. XPWord apwSol[MAXSOL];                   /* the answers */
  904. Xint cpwLast;
  905. X
  906. XDebug(
  907. Xvoid DumpWord(Quad *pq) {
  908. X    int i;
  909. X    Quad q;
  910. X    for (i = 0; i < ALPHABET; i++) {
  911. X        if (alPhrase[i].uFrequency == 0) continue;
  912. X        q = pq[alPhrase[i].iq];
  913. X        if (alPhrase[i].uShift) q >>= alPhrase[i].uShift;
  914. X        q &= alPhrase[i].uBits;
  915. X        while (q--) putchar('a'+i);
  916. X    }
  917. X    putchar(' ');
  918. X}
  919. X)                                       /* End of debug code */
  920. X
  921. Xvoid DumpWords(void) {
  922. X    int i;
  923. X    for (i = 0; i < cpwLast; i++) wprint(apwSol[i]->pchWord);
  924. X    printf("\n");
  925. X}
  926. X
  927. XStat(unsigned long ulHighCount; unsigned long ulLowCount;)
  928. X
  929. Xjmp_buf jbAnagram;
  930. X
  931. X#define OneStep(i) \
  932. X    if ((aqNext[i] = pqMask[i] - pw->aqMask[i]) & aqMainSign[i]) goto fail;
  933. X
  934. X#ifndef ASM_ANAGRAM
  935. Xvoid FindAnagram(Quad *pqMask, register PPWord ppwStart, int iLetter) {
  936. X    Quad aqNext[MAX_QUADS];
  937. X    register PWord pw;
  938. X    Quad qMask;
  939. X    unsigned iq;
  940. X    PPWord ppwEnd = &apwCand[cpwCand];
  941. X
  942. X    if (HaltProcessing()) longjmp(jbAnagram, 1);
  943. X
  944. X    Debug(printf("Trying :"); DumpWord(pqMask); printf(":\n");)
  945. X
  946. X    for (;;) {
  947. X        iq = alPhrase[achByFrequency[iLetter]].iq;
  948. X        qMask = alPhrase[achByFrequency[iLetter]].uBits <<
  949. X                alPhrase[achByFrequency[iLetter]].uShift;
  950. X        if (pqMask[iq] & qMask) break;
  951. X        iLetter++;
  952. X    }
  953. X
  954. X    Debug(printf("Pivoting on %c\n", i2ch(achByFrequency[iLetter]));)
  955. X
  956. X    while (ppwStart < ppwEnd) {          /* Half of the program execution */
  957. X        pw = *ppwStart;                  /* time is spent in these three */
  958. X
  959. X        Stat(if (++ulLowCount == 0) ++ulHighCount;)
  960. X
  961. X#if MAX_QUADS > 0
  962. X        OneStep(0);                     /* lines of code. */
  963. X#endif
  964. X
  965. X#if MAX_QUADS > 1
  966. X        OneStep(1);
  967. X#endif
  968. X
  969. X#if MAX_QUADS > 2
  970. X        OneStep(2);
  971. X#endif
  972. X
  973. X#if MAX_QUADS > 3
  974. X        OneStep(3);
  975. X#endif
  976. X
  977. X#if MAX_QUADS > 4
  978. X            @@"Add more unrolling steps here, please."@@
  979. X#endif
  980. X
  981. X        /* If the pivot letter isn't present, defer this word until later */
  982. X        if ((pw->aqMask[iq] & qMask) == 0) {
  983. X            *ppwStart = *--ppwEnd;
  984. X            *ppwEnd = pw;
  985. X            continue;
  986. X        }
  987. X
  988. X        /* If we get here, this means the word fits. */
  989. X        apwSol[cpwLast++] = pw;
  990. X        if (cchPhraseLength -= pw->cchLength) { /* recurse */
  991. X            Debug(DumpWords();)
  992. X            /* The recursive call scrambles the tail, so we have to be
  993. X             * pessimistic.
  994. X             */
  995. X            ppwEnd = &apwCand[cpwCand];
  996. X            FindAnagram(aqNext, ppwStart, iLetter);
  997. X        } else DumpWords();             /* found one */
  998. X        cchPhraseLength += pw->cchLength;
  999. X        --cpwLast;
  1000. Xfail:
  1001. X        ppwStart++;
  1002. X        continue;
  1003. X    }
  1004. X}
  1005. X#else
  1006. Xextern void FindAnagram(Quad *pqMask, register PPWord ppwStart, int iLetter);
  1007. X#endif
  1008. X
  1009. Xint Cdecl CompareFrequency(char *pch1, char *pch2) {
  1010. X    return auGlobalFrequency[*pch1] < auGlobalFrequency[*pch2]
  1011. X        ?  -1 :
  1012. X           auGlobalFrequency[*pch1] == auGlobalFrequency[*pch2]
  1013. X        ?   0 : 1;
  1014. X}
  1015. X
  1016. Xvoid SortCandidates(void) {
  1017. X    int i;
  1018. X
  1019. X    /* Sort the letters by frequency */
  1020. X    for (i = 0; i < ALPHABET; i++) achByFrequency[i] = i;
  1021. X    qsort((char *)achByFrequency, ALPHABET, sizeof(achByFrequency[0]),
  1022. X          CompareFrequency);
  1023. X
  1024. X    fprintf(stderr, "Order of search will be ");
  1025. X    for (i = 0; i < ALPHABET; i++) fputc(i2ch(achByFrequency[i]), stderr);
  1026. X    fputc('\n', stderr);
  1027. X}
  1028. X
  1029. Xint fInteractive;
  1030. X
  1031. Xchar *GetPhrase(char *pch) {
  1032. X    if (fInteractive) printf(">");
  1033. X    fflush(stdout);
  1034. X    return gets(pch);
  1035. X}
  1036. X
  1037. Xint Cdecl main(int cpchArgc, char **ppchArgv) {
  1038. X    char achPhrase[255];
  1039. X
  1040. X    if (cpchArgc != 2 && cpchArgc != 3)
  1041. X        Fatal("Usage: anagram dictionary [len]\n", 0);
  1042. X
  1043. X    if (cpchArgc == 3) cchMinLength = atoi(ppchArgv[2]);
  1044. X
  1045. X    fInteractive = isatty(1);
  1046. X
  1047. X    ReadDict(ppchArgv[1]);
  1048. X
  1049. X    while (GetPhrase(achPhrase)) {
  1050. X        if (isdigit(achPhrase[0])) {
  1051. X            cchMinLength = atoi(achPhrase);
  1052. X            printf("New length: %d\n", cchMinLength);
  1053. X        } else if (achPhrase[0] == '?') {
  1054. X            DumpCandidates();
  1055. X        } else {
  1056. X            BuildMask(achPhrase);
  1057. X            AddWords();
  1058. X            if (cpwCand == 0 || cchPhraseLength == 0) continue;
  1059. X
  1060. X            Stat(ulHighCount = ulLowCount = 0;)
  1061. X            cpwLast = 0;
  1062. X            SortCandidates();
  1063. X            if (setjmp(jbAnagram) == 0)
  1064. X                FindAnagram(aqMainMask, &apwCand[0], 0);
  1065. X            Stat(printf("%lu:%lu probes\n", ulHighCount, ulLowCount);)
  1066. X        }
  1067. X    }
  1068. X    return 0;
  1069. X}
  1070. END_OF_FILE
  1071. if test 20332 -ne `wc -c <'anagram.c'`; then
  1072.     echo shar: \"'anagram.c'\" unpacked with wrong size!
  1073. fi
  1074. # end of 'anagram.c'
  1075. fi
  1076. if test -f 'unproto.pl' -a "${1}" != "-c" ; then 
  1077.   echo shar: Will not clobber existing file \"'unproto.pl'\"
  1078. else
  1079. echo shar: Extracting \"'unproto.pl'\" \(3182 characters\)
  1080. sed "s/^X//" >'unproto.pl' <<'END_OF_FILE'
  1081. X# [$Id: unproto.pl 1.1 91/08/18 15:57:55 raymond Exp Locker: raymond $]
  1082. X#
  1083. X# This filter converts ANSI-like declarations into K&R-like declarations.
  1084. X# But only if they are written in my style.  Namely...
  1085. X#
  1086. X# Function headers are of the form
  1087. X#
  1088. X#    <type><function>(<prototype>) { /* optional comment */
  1089. X#
  1090. X# Optionally prefixed by the word `Static' and with the word `Cdecl'
  1091. X# optionally inserted before the function name.  For example,
  1092. X#
  1093. X#       Static int Cdecl smpDoSomething(int iWhat, char *pchReason) {
  1094. X#
  1095. X# Declarations are of the form
  1096. X#
  1097. X#    <type> <function>(<prototype>); /* optional comment */
  1098. X#
  1099. X# for example,
  1100. X#
  1101. X#    int smpDoSomething(int iWhat, char *pchReason);
  1102. X#
  1103. X# If a declaration or definition spans more than one line, put the comment
  1104. X# /*#*/ at the end of all nonterminal lines.  For example,
  1105. X#
  1106. X#    int smpDoSomething(int iWhat,        /*#*/
  1107. X#               char *pchReason) {
  1108. X#
  1109. X# Vararg function definitions must fit on one line.
  1110. X#
  1111. X# SPACES ARE CRUCIAL.  Observe where the spaces are placed in the @types
  1112. X# array.  The spaces must be placed identically in your declaration also.
  1113. X#
  1114. X# To explain how vararg functions are converted would take more time than
  1115. X# giving an example and telling you `just like this'.
  1116. X#
  1117. X# ANSI
  1118. X#
  1119. X# int whatever(int i, char *pch, ...) {
  1120. X#   va_list ap;
  1121. X#   [...]
  1122. X#   va_start(ap, pch);
  1123. X#   [...]
  1124. X# }
  1125. X#
  1126. X# K&R
  1127. X#
  1128. X# int whatever(va_alist) va_dcl { int i; char *pch;
  1129. X#   va_list ap;
  1130. X#   [...]
  1131. X#   va_start(ap); i = va_arg(ap, int); pch = va_arg(ap, char *);
  1132. X#   [...]
  1133. X# }
  1134. X#
  1135. X# IMPORTANT!  Your va_list variable must be named `ap'.
  1136. X#
  1137. X#
  1138. X#
  1139. X# Here are the data types used in the program.
  1140. X
  1141. X@types = ('void ', 'int ', 'unsigned ', 'char ',
  1142. X          'Word ', 'PWord ', 'void *', 'char *', 'char **');
  1143. X
  1144. Xgrep(s/\*/\\*/, @types);        # make them regexps
  1145. X$type_regexp = join('|', @types);
  1146. X
  1147. X$apstart = "BUGBUG";
  1148. X
  1149. Xwhile (<>) {
  1150. X    while (s|\s*/\*#\*/$| @|) { chop; $_ .= <>; }  # collect continuations
  1151. X
  1152. X    print 'Static ' if s/^Static //;
  1153. X    if (/va_start/) {            # convert vararg start
  1154. X    s/va_start\(ap, \w+\);/va_start(ap); $apstart/;
  1155. X    }
  1156. X
  1157. X    unless (s/^($type_regexp)((Cdecl )?\w+)\((.*)\)( {|;)//) { print; next; }
  1158. X
  1159. X    $apstart = "BUGBUG";
  1160. X
  1161. X    # $1 = type, $2 = "Cdecl ",
  1162. X    # $3 = name, $4 = prototype, $5 = declaration or definition
  1163. X
  1164. X    # found a prototype
  1165. X    print $1, $2, "(";
  1166. X
  1167. X    # if a declaration, then emit nothing
  1168. X    if ($5 eq ";") { print "); $_"; next; }
  1169. X
  1170. X    # parse the prototype
  1171. X    $proto = $4;
  1172. X    if ($proto eq "void") {        # no arguments
  1173. X    $post = "";
  1174. X    } elsif ($proto =~ s/\.\.\.$//) {    # varargs!
  1175. X    print "va_alist";        # vararg nonsense
  1176. X    $post = $proto;         # post-declarations
  1177. X        $post =~ y/,/;/;
  1178. X    $_ = $post . $_;        # these go after the brace
  1179. X    $post = "va_dcl";        # new post
  1180. X
  1181. X    @var = split(/, /, $proto);    # get list of variables
  1182. X    for (@var) {            # for each variable
  1183. X        s/^(.*\W)//;        # chop off the type specifier
  1184. X        $_ .= " = va_arg(ap, $1);"; # convert to a va_arg
  1185. X    }
  1186. X    $apstart = join("", @var);
  1187. X    } else {                # build arg list
  1188. X    $post = $proto . ";";        # post-declarations
  1189. X    $post =~ y/,@/;\n/;
  1190. X    @var = split(/, /, $proto);    # get list of variables
  1191. X    grep(s/^.*\W//, @var);        # strip off the gunk
  1192. X    print join(",", @var);
  1193. X    }
  1194. X    print ") $post { $_";
  1195. X}
  1196. END_OF_FILE
  1197. if test 3182 -ne `wc -c <'unproto.pl'`; then
  1198.     echo shar: \"'unproto.pl'\" unpacked with wrong size!
  1199. fi
  1200. # end of 'unproto.pl'
  1201. fi
  1202. echo shar: End of shell archive.
  1203. exit 0
  1204.