treeprint.w
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:7k
源码类别:

通讯编程

开发平台:

Visual C++

  1. defcovernote{Copyright 1987 Norman Ramsey -- Princeton University}
  2. defvbar{.{|}}
  3. @*Directory Trees.
  4. Our object is to print out a directory hierarchy in some pleasant way.
  5. The program takes output from {tt find * -type d -print vbar sort}
  6. @^system dependencies@>
  7. and produces a nicer-looking listing.
  8. More precisely, our input, which is the output of {tt find} followed
  9. by {tt sort}, is a list of fully qualified directory names (parent
  10. and child separated by slashes |'/'|); everything has already been
  11. sorted nicely into lexicographic order.
  12. The {tt treeprint} routine takes one option, |"-p"|, which tells it
  13. to use the printer's line-drawing set, rather than the terminal's.
  14. @c
  15. @<Global definitions@>@;
  16. @<Global include files@>@;
  17. @<Global declarations@>@;
  18. @#
  19. main(argc, argv)
  20.      int argc;
  21.      char **argv;
  22. {
  23. @<|main| variable declarations@>;
  24. @<Search for options and set special characters on |"-p"|@>;
  25. @<Read output from find and enter into tree@>;
  26. @<Write tree on standard output@>@;
  27. exit(0);
  28. }
  29. @
  30. We make all the siblings of a directory a linked list off of its left child, 
  31. and the offspring a linked list off the right side.
  32. Data are just directory names.
  33. @d sibling left
  34. @d child right
  35. @<Global decl...@>=
  36. typedef struct tnode {
  37.   struct tnode *left, *right;
  38.   char *data;
  39. } TNODE;
  40. @ @<|main| variable...@>=
  41. struct tnode *root=NULL;
  42. @*Input.
  43. Reading the tree is simple---we read one line at a time, and call on the 
  44. recursive |add_tree| procedure.
  45. @c
  46. read_tree (fp, rootptr)
  47.    FILE *fp;
  48.    struct tnode **rootptr;
  49. {
  50.    char buf[255], *p;
  51.    while ((fgets(buf, 255, fp))!=NULL) {
  52.      @<If |buf| contains a newline, make it end there@>;
  53.      add_tree(rootptr, buf);
  54.    }
  55.  }
  56. @ @<Global include...@>=
  57. #include <stdio.h>
  58. @ Depending what system you're on, you may or may not get a newline in |buf|.
  59. @<If |buf| contains a newline...@>=
  60.      p=buf; while (*p!=''&&*p!='n') p++;
  61. @^system dependencies@>
  62.      *p='';
  63. To add a string, we split off the first part of the name and insert it into 
  64. the sibling list. We then do the rest of the string as a child of the new node.
  65. @c
  66. add_tree(rootptr, p)
  67.      struct tnode **rootptr;
  68.      char *p;
  69. {
  70.      char *s;
  71.      int slashed;
  72.      if (*p=='') return;
  73. @<Break up the string so |p| is the first word,
  74.      |s| points at null-begun remainder,
  75.     and |slashed| tells whether |*s=='/'| on entry@>;
  76.      if (*rootptr==NULL) {
  77. @<Allocate new node to hold string of size |strlen(p)|@>;
  78.        strcpy((*rootptr)->data,p);
  79.      } 
  80.      if (strcmp((*rootptr)->data,p)==0) {
  81.            if (slashed) ++s;
  82.            add_tree(&((*rootptr)->child),s);
  83.  }
  84.        else {
  85.            if (slashed) *s='/';
  86.            add_tree(&((*rootptr)->sibling),p);
  87.  }
  88.    }
  89. @ We perform some nonsense to cut off the string |p| so that |p| just
  90. holds the first word of a multiword name. Variable |s| points at what
  91. was either the end of |p| or a slash delimiting names. In either case
  92. |*s| is made |''|.  Later, depending on whether we want to pass the
  93. whole string or the last piece, we will restore the slash or advance
  94. |s| one character to the right.
  95. @<Break up...@>=
  96.      for (s=p;*s!=''&&*s!='/';) s++;
  97.      if (*s=='/') {
  98.        slashed=1;
  99.        *s='';
  100.      } else slashed=0;
  101. @ Node allocation is perfectly standard dots
  102. @<Allocate new node...@>=
  103.        *rootptr=(struct tnode *) malloc (sizeof(struct tnode));
  104.        (*rootptr)->left = (*rootptr)->right = NULL;
  105.        (*rootptr)->data = malloc (strlen(p)+1);
  106. @
  107. @<Global decl...@>= char *malloc();
  108. @ In this simple implementation, we just read from standard input.
  109. @<Read...@>= read_tree(stdin,&root);
  110. @*Output.
  111. We begin by defining some lines, tees, and corners.
  112. The |s| stands for screen and the |p| for printer.
  113. You will have to change this for your line-drawing set.
  114. @^system dependencies@>
  115. @<Global definitions@>=
  116. #define svert '|'
  117. #define shoriz '-'
  118. #define scross '+'
  119. #define scorner '\' /* lower left corner */
  120. #define pvert '|'
  121. #define phoriz '-'
  122. #define pcross '+'
  123. #define pcorner '\' /* lower left corner */
  124. @ The default is to use the terminal's line drawing set.
  125. @<Global declarations@>=
  126. char vert=svert;
  127. char horiz=shoriz;
  128. char cross=scross;
  129. char corner=scorner;
  130. @ With option |"-p"| use the printer character set.
  131. @<Search for options...@>=
  132. while (--argc>0) {
  133.   if (**++argv=='-') {
  134.     switch (*++(*argv)) {
  135.       case 'p': 
  136.         vert=pvert;
  137.         horiz=phoriz;
  138.         cross=pcross;
  139.         corner=pcorner;
  140. break;
  141.       default:
  142.         fprintf(stderr,"treeprint: bad option -%cn",**argv);
  143. break;
  144.       }
  145.   }
  146. }
  147. @ We play games with a character stack to figure out when to put in vertical
  148. bars.
  149. A vertical bar connects every sibling with its successor, but the last sibling
  150. in a list is followed by blanks, not by vertical bars. The state of 
  151. bar-ness or space-ness for each preceding sibling is recorded in the 
  152. |indent_string| variable, one character (bar or blank) per sibling.
  153. @<Global decl...@>=
  154. char indent_string[100]="";
  155. @  Children get printed 
  156. before siblings.
  157. We don't bother trying to bring children up to the same line as their parents,
  158. because the UNIX/ filenames are so long.
  159. We define a predicate telling us when a sibling is the last in a series.
  160. @d is_last(S) (S->sibling==NULL)
  161. @c
  162. print_node(fp, indent_string, node)
  163.      FILE *fp;
  164.      char *indent_string;
  165.      struct tnode *node;
  166. {
  167.   char string[255];
  168.      int i;
  169.      char *p, *is;
  170.   if (node==NULL) {
  171.   }
  172.   else {
  173.     *string='';
  174.     for (i=strlen(indent_string); i>0; i--) 
  175.       strcat(string,@,      " |  ");
  176.     strcat(string,@t  @>  " +--");
  177. @<Replace chars in |string| with chars from 
  178.          line-drawing set and from |indent_string|@>;
  179.     fprintf(fp,"%s%sn",string,node->data);
  180. @#
  181.     /* Add vertical bar or space for this sibling (claim |*is==''|) */
  182.     *is++ = (is_last(node) ? ' ' : vert);
  183.     *is='';
  184.    
  185.     print_node(fp, indent_string, node->child); /* extended |indent_string| */
  186.     *--is='';
  187.     print_node(fp, indent_string, node->sibling); /* original |indent_string| */
  188.   }
  189. }
  190. @ For simplicity, we originally wrote connecting lines with |'|'|, |'+'|, and
  191. |'-'|.
  192. Now we replace those characters with appropriate characters from the 
  193. line-drawing set. 
  194. We take the early vertical bars and replace them with characters from
  195. |indent_string|, and we replace the other characters appropriately.
  196. We are sure to put a |corner|, not a |cross|, on the last sibling in 
  197. a group.
  198. @<Replace chars...@>=
  199.     is=indent_string;
  200.     for (p=string; *p!=''; p++) switch(*p) {
  201.        case '|': *p=*is++; break;
  202.        case '+': *p=(is_last(node) ? corner : cross); break;
  203.        case '-': *p=horiz; break;
  204.        default: break;
  205.        }
  206. @ For this simple implementation, we just write on standard output.
  207. @<Write...@>= print_node(stdout, indent_string, root);
  208. @*Index.