home *** CD-ROM | disk | FTP | other *** search
/ ftp.cs.arizona.edu / ftp.cs.arizona.edu.tar / ftp.cs.arizona.edu / icon / historic / v941.tgz / icon.v941src.tar / icon.v941src / ipl / progs / format.icn < prev    next >
Text File  |  2002-03-26  |  5KB  |  163 lines

  1. ############################################################################
  2. #
  3. #    File:     format.icn
  4. #
  5. #    Subject:  Program to word wrap a range of text
  6. #
  7. #    Author:   Robert J. Alexander
  8. #
  9. #    Date:     March 26, 2002
  10. #
  11. ############################################################################
  12. #
  13. #   This file is in the public domain.
  14. #
  15. ############################################################################
  16. #
  17. #  Filter to word wrap a range of text.
  18. #
  19. #  A number of options are available, including full  justification (see
  20. #  usage text,  below).  All lines that have the same indentation as the
  21. #  first  line (or same  comment leading character format if  -c option)
  22. #  are wrapped.  Other lines are left as is.
  23. #
  24. #  This  program  is useful in conjunction with editors  that can invoke
  25. #  filters on a range of selected text.
  26. #
  27. #  The -c option attempts to establish the form of a comment based on the
  28. #  first  line, then does its best to deal  properly  with the following
  29. #  lines.   The  types of  comment lines that  are handled  are those in
  30. #  which  each  line starts with a "comment" character string  (possibly
  31. #  preceded  by  spaces).  While formatting  comment  lines, text  lines
  32. #  following the prototype line that don't match the  prototype  but are
  33. #  flush with  the  left margin are also  formatted  as  comments.  This
  34. #  feature simplifies  initially entering  lengthy  comments  or  making
  35. #  major modifications, since  new  text can  be entered without concern
  36. #  for comment formatting, which will be done automatically later.
  37. #
  38. ############################################################################
  39. #
  40. #  Links: options
  41. #
  42. ############################################################################
  43.  
  44. link options
  45.  
  46. procedure main(arg)
  47.    local usage, opts, tabs, comment, format, just1, space, nspace, wchar, Entab
  48.    local line, pre, empty, outline, spaces, word, len, width, xspace, Detab
  49.    local outpre
  50.    #
  51.    #  Process the options.
  52.    #
  53.    usage := 
  54.      "usage: format [-options]\n_
  55.             \t-w N\tspecify line width (default 72)\n_
  56.             \t-t N\tspecify tab width (default 8)\n_
  57.             \t-j\tfully justify lines\n_
  58.             \t-J\tfully justify last line, too\n_
  59.             \t-c\tattempt to format program comments\n_
  60.             \t-n\tdon't put extra spaces after sentences\n_
  61.             \t-h\tprint help message"
  62.    opts := options(arg,"ht+w+cjJn")
  63.    if \opts["h"] then stop(usage)
  64.    width := integer(\opts["w"]) | 72
  65.    tabs := (integer(\opts["t"]) | 8) + 1
  66.    if tabs >= 2 then {
  67.       Detab := detab
  68.       Entab := entab
  69.       }
  70.    else Entab := Detab := 1
  71.    comment := opts["c"]
  72.    format := if \just1 | \opts["j"] then justify else 1
  73.    just1 := opts["J"]
  74.    xspace := if \opts["s"] then '' else '.?:!'
  75.    #
  76.    #  Initialize variables.
  77.    #
  78.    space := ' \t'
  79.    nspace := ~space
  80.    wchar := nspace
  81.    #
  82.    #  Read the first line to establish a prototype of comment format
  83.    #  if -c option, or of leading spaces if normal formatting.
  84.    #
  85.    line := Detab(read(),tabs) | exit()
  86.    line ?
  87.       pre := (tab(many(space)) | "") ||
  88.      if \comment then
  89.         tab(many(nspace)) || tab(many(space)) |
  90.             stop("### Can't establish comment pattern")
  91.      else
  92.         ""
  93.    width -:= *pre
  94.    empty := trim(pre)
  95.    outpre := Entab(pre,tabs)
  96.    outline := spaces := ""
  97.    repeat {
  98.       line ? {
  99.      #
  100.      #  If this line indicates a formatting break...
  101.      #
  102.      if (=empty & pos(0)) | (=pre & any(space) | pos(0)) |
  103.             (/comment & not match(pre)) then {
  104.         write(outpre,"" ~== outline)
  105.         outline := spaces := ""
  106.         write(line)
  107.         }
  108.      #
  109.      #  Otherwise continue formatting.
  110.      #
  111.      else {
  112.         =pre
  113.         tab(0) ? {
  114.            tab(many(space))
  115.            while word := tab(many(wchar)) & (tab(many(space)) | "") do {
  116.           if *outline + *spaces + *word > width then {
  117.              write(outpre,"" ~== format(outline,width))
  118.              outline := spaces := ""
  119.              }
  120.           outline ||:= spaces || word
  121.           spaces := if any(xspace,word[-1]) then "  " else " "
  122.           }
  123.            }
  124.         }
  125.      }
  126.       line := Detab(read(),tabs) | break
  127.       }
  128.    write(outpre,"" ~== (if \just1 then justify else 1)(outline,width))
  129. end
  130.  
  131.  
  132. #
  133. #  justify(s,width) -- Inserts extra spaces between words of "s" so that
  134. #  "s" will  be exactly  "width" characters  long.   "s" is  trimmed  of
  135. #  spaces  on the right  and left ends.  If  "s" contains fewer than two
  136. #  words, or if the trimmed version is longer than "width", the  trimmed
  137. #  version  of "s" is returned unchanged.  Where some gaps between words
  138. #  are  required  to  be   wider  than  others,  the  extra  spaces  are
  139. #  distributed randomly to minimize "rivering" in justified paragraphs.
  140. #
  141. procedure justify(s,width)
  142.    local wlist,wset,t,r
  143.    static space,nspace
  144.    initial {
  145.       space := ' '
  146.       nspace := &cset -- space
  147.       }
  148.    s := trim(s[many(space,s) | 1:0])
  149.    wlist := []
  150.    s ? while put(wlist,[tab(many(nspace)),*tab(many(space)) | 0])
  151.    if *s >= width | *wlist < 2 then return s
  152.    wset := set(wlist[1:-1])
  153.    t := (width - *s) / *wset
  154.    every (!wset)[2] +:= t
  155.    every 1 to (width - *s) % *wset do {
  156.       (t := ?wset)[2] +:= 1
  157.       delete(wset,t)
  158.       }
  159.    r := ""
  160.    every t := !wlist do r ||:= t[1] || repl(" ",t[2])
  161.    return r
  162. end
  163.