mark@zok.UUCP (Mark W. Snitily) (11/01/90)
Note: I'm posting this for a friend that doesn't have regular net access. Cecil McGregor is the author. As the subject line says, this is a program that builds a symbolic link tree. It recognizes RCS & SCCS subdirectories and symlinks to the directory instead of all of the individual RCS or SCCS files. The program is in use on sun[34]'s running Sun OS 4.0.3. Questions can be emailed to me and I'll forward them on to Cecil. -- Mark Mark W. Snitily Consulting Services: 894 Brookgrove Lane Graphics, Operating Systems, Compilers Cupertino, CA 95014 (408) 252-0456 mark@zok.uucp West Coast UUCP X11 archive site If your mailer doesn't like the .uucp domain, these also work: ...!{mips,sgi}!zok!mark, mark%zok@mips.com, mark%zok@sgi.com ---<cut here>------------------------------------------------------------------ #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 1 (of 1)." # Contents: lktree.1 lktree.c # Wrapped by mark@zok on Wed Oct 31 23:51:41 1990 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'lktree.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'lktree.1'\" else echo shar: Extracting \"'lktree.1'\" \(7019 characters\) sed "s/^X//" >'lktree.1' <<'END_OF_FILE' X... X... $Header: /wrld/6e50/cecil/tools/RCS/lktree.1,v 1.3 89/08/04 17:03:23 cecil Exp $ X... X... $Log: lktree.1,v $ XRevision 1.3 89/08/04 17:03:23 cecil XAdded comments for comparing lktree to 'cp -r' command. X XRevision 1.2 89/08/04 09:53:11 cecil XAdded information for multiple runs of lktree on the same Xdevelopment tree. XAdded "scary messages" to BUGS XAdded Author section since people did not believe Cecil Xprogrammed this! X XRevision 1.1 89/08/03 14:17:12 cecil XInitial revision X X... X.TH LKTREE 1C "4 Aug 89" X.SH NAME X.sp .5 Xlktree \- link tree builder X.SH SYNOPSIS X.sp .5 X.B lktree X.B [ X.I \ root_tree X.B \-I X.I incl_file X.B \ \-X X.I excl_file X.I root_tree X.B ] X.SH DESCRIPTION X.IX "lktree command" "" "\fLlktree\fP \(em link tree builder" X.I lktree Xgenerates a topologically equivalent tree at the current location Xthat corresponds to the directory structure at X.I root_tree XThe X.I incl_file Xand X.I excl_file Xare file matching arguements Xthat are used to include and exclude files from the tree Xlinking operations. X.LP X.I lktree Xis similar to X.I cp \-r Xin that it recursively copies directory structures. The difference Xis that X.I lktree Xis selective in the files in includes and excludes. X.I lktree Xalso builds links rather than performing file copies. X.I lktree Xknows that X.B RCS Xwill be used to maintain a programming development system for use Xby multiple programmers. Thus all X.B RCS Xdirectories are linked to the original development tree while Xall other directories are actually created in the programmers Xarea. This allows for natural use of X.B RCS Xcommands that are (hopefully) transparent to development. X.LP XIf a X.I root_tree Xdoes not exist, the environment variable X.BI "ROOT_TREE" Xis used to anchor the topological structure. X.BI "ROOT_TREE" Xis used to provide easy transition to multiple projects Xin a portable, configurable manner. X.LP XThe X.I root_tree Xshould be a fully qualified directory name for a currently Xexisting directory. It is an error to do otherwise. X.IR incl_file Xand X.IR excl_file Xdo not have to be present. If these two files are Xmissing then an open attempt is made on X.BI $ROOT_TREE X/incl_file Xand X.BI $ROOT_TREE X/excl_file. This allows defaulting Xof the non-essential files in a project-wide manner. X X.I lktree Xis designed to be used with no parameters; it is the Xresponsibility of the system adminstrator to configure Xprogrammer .cshrc files to contain the necessary Xenvironment. Programmers working on multiple Xprojects should access the X.BI ROOT_TREE X/.cshrc for particular tree needed. The local project X.BI ROOT_TREE X/.cshrc Xcontains the environmental setting to access everything Xabout that project. X.LP X.B RCS Xdirectories are a specail case. The intent of this utility Xis to provide a convenient method for programmers to Xhave separate development trees and to use X.B RCS Xas a backup feature and configuration management utility. X.B RCS Xis also used to insure that only one programmer at a time has Xa file checked out. X.LP XWith the linked X.B RCS Xdirectory the programmer has easy access to the X.B RCS X.I co Xand X.I ci Xutilities. XAfter building the link tree, the programmer will notice that Xall files are linked to X.B $ROOT_TREE Xfiles, while the programmer has true directories. Furthermore, XRCS exists as a link, not as a directory. This link allows Xmultiple programmers to check out RCS files in an orderly manner. XAfter developing and adding modules to the root tree Xthe new modules must be distributed to the development team. XThe is done by simply running X.I lktree Xonce again in each developer's base development directory. XThis will "refresh" the developer's tree to match the X.B $ROOT_TREE Xfiles. Specifically files that are locked by developers Xwill not be disturbed. X.LP XProgrammers will note that object files are neither copied Xnor symbolically linked. This allows individual programmers Xto have their own copy of an object file. Each programmer Xmust "make" his own tree in order to obtain executables. X.LP X.I incl_file Xis normally in the X.BI ROOT_TREE Xdirectory and contains an X.I fgrep Xlist of files to be Xincluded in the linking operation. An example that selects Xall c, h and makefiles is: X.RS X.nf X.cc @ X.c Xmake X.h X@cc . X.fi X.RE XThe leading period insures that files the "dot c and h" files Xare the files selected, not files that simply contain a c or h. X X.I excl_file Xis normally in the X.BI ROOT_TREE Xdirectory and contains a Xsimilar fgrep file of files to be excluded to the link Xtree being built. The X.I excl_file Xis applied to the file candidates after X.I incl_file Xselects a list of possible candidates to linking. XThe X.I excl_file Xis meant to exclude directories such as X.B SCCS Xfrom linking. An example that excludes all X.B SCCS Xdirectories, Xall X.B BAK Xfiles (as output from X.I indent X), and miscellaneous non-essential files (generated by cross compilers): X.RS X.nf XSCCS XBAK Xlst Xobj Xmrg Xxrf Xcrf X.fi X.RE X.SH FILES XDefault files are assumed in the environment root tree X.BI $ROOT_TREE. X.I incl_file Xand X.I excl_file Xare defined by the configuration manager. X.BI $ROOT_TREE Xitself is an environmental variable that is a directory to the Xbaseline of source code to be used by multiple Xprogrammers in software development. It is not necessarily Xrestricted to pure code, but may also include documentation. X.SH AUTHOR XCecil McGregor X.SH SEE ALSO Xdco(1c), dci(1c) and the configuration manager's procedures. X.SH BUGS X\fIlktree\fP is meant to create link trees on a single file Xserver, attempting X.I lktree Xacross file servers is untested. X.LP X.I lktree Xcopies to the current directory, programmers should create Xtheir root directory, descend into it and X.I then Xperform X.I lktree Xoperations. X.LP X.I lktree Xwill work correctly on an already existing link tree. XThis is useful when new modules are added to the currently Xexisting X.BI ROOT_TREE Xand developers must work with them. X.LP X.I lktree Xrequires permission and access bits to be set properly on Xthe X.BI ROOT_TREE Xso programmers can read files. X.LP X.I lktree Xassumes that the system adminstrator performs backups on Xprogrammer link trees as well as X.BI ROOT_TREE. X.LP XWhen rerunning X.I lktree Xin an already built tree many scary messages are generated. XThese messages should be suppressed and only the successful Xlinks reported. This would give the programmer confidence that Xthe system is operating properly as well as inform him of Xthe new links that were performed. X.LP XOrphan links might be created when running X.I lktree Xmultiple times on the same root tree. This needs a utility Xto inform the development programmer that there is an orphan. XAn orphan link is a file that originally existed in the Xroot development tree, but was either renamed or removed. XThe development tree retains the symbolic link to the original Xfile but an attempted access to it will result in an error Xmessage. It should be the responsibility of the configuration Xmanager to resolve these problems; the orphan-link utility Xis simply part of his tool kit. X.ex END_OF_FILE if test 7019 -ne `wc -c <'lktree.1'`; then echo shar: \"'lktree.1'\" unpacked with wrong size! fi # end of 'lktree.1' fi if test -f 'lktree.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'lktree.c'\" else echo shar: Extracting \"'lktree.c'\" \(8287 characters\) sed "s/^X//" >'lktree.c' <<'END_OF_FILE' Xstatic char RcsId[] = "$Header: /wrld/6e50/cecil/tools/RCS/lktree.c,v 1.2 89/08/04 10:40:32 cecil Exp Locker: cecil $"; X/* X $Log: lktree.c,v $ X * Revision 1.2 89/08/04 10:40:32 cecil X * adding lktree to RCS control. X * X * Revision 1.1 89/08/04 08:50:07 cecil X * Initial revision X * X*/ X X#include <stdio.h> X#include <ftw.h> X#include <sys/types.h> X#include <sys/stat.h> X#include <string.h> X XFILE *fp; X Xint debug = 0; /* TRUE to debug */ X#define DEBUG(x) {if(debug)fprintf(stderr, "%s:%d:%s\n", __FILE__,__LINE__,x);} X Xint dir_ignore = 0; /* option -d == ignore directories */ Xint link_ignore = 0; /* option -l == ignore links */ X Xchar *head; /* the requested root head */ Xchar head_buf[512]; /* work buffer for environmental variable */ X XFILE *out_fp; /* temp file for fgrep's */ Xchar *temp_fn; /* return from mktemp() */ X XFILE *incl_fp = 0, *excl_fp = 0; /* include and exclude file ptrs */ Xchar *incl_fn, *excl_fn; /* include and exclude file names */ X Xchar *rcs_root_fn; /* name of root directory to start link */ X X/* strdup - duplicate a string */ Xchar *strdup(s) Xchar *s; X{ X char *cp; X X cp = (char *)malloc(strlen(s)+1); X strcpy(cp, s); X return cp; X} X X/*==================================================================*/ X/* strsearch - search string for sub_string, return ptr to start of */ X/* sub_string within string, NULL if not within. */ X/*==================================================================*/ X Xchar *strsearch(string, sub_string) Xchar *string, *sub_string; X{ X for(;string = (char *)index(string, *sub_string);++string){ X /* found first char of substring in string */ X if(strcmp(string, sub_string) == 0) X return string; X } X /* substring is not within string, return NULL */ X return (char *)0; X} X Xint print_file(fn, sbuf, ftype) Xchar *fn; /* name of file */ Xstruct stat *sbuf; Xint ftype; X{ X register char *name; X char buf[BUFSIZ]; X char *temp; X int status; X X DEBUG(fn); X /* If this file is a symbolic link, then absolutely */ X /* ignore this reference! */ X /* This call succeeds only if fn IS a link. */ X if((status = readlink(fn, buf, BUFSIZ)) != -1){ X DEBUG("ignoring symbolic link file"); X return 0; X } X X /* Never descend into RCS directories, as the intent */ X /* is to link an RCS directory instead ths individual*/ X /* files in the directory. */ X if(re_exec(fn) != 0) { X DEBUG("NOT descending into RCS dir"); X return 0; X } X X /* must run lstat on the file */ X lstat(fn, sbuf); X X if ((sbuf->st_mode & S_IFMT) == S_IFBLK) X return 0; X if ((sbuf->st_mode & S_IFMT) == S_IFSOCK) X return 0; X if ((sbuf->st_mode & S_IFMT) == S_IFIFO) X return 0; X if ((sbuf->st_mode & S_IFMT) == S_IFCHR) X return 0; X if ((sbuf->st_mode & S_IFMT) == S_IFDIR) { X struct stat xbuf; X X if (dir_ignore) X return 0; X X stat(fn, &xbuf); X /* if this is a link to a directory, ignore */ X if ((xbuf.st_mode & S_IFMT) == S_IFLNK) { X return 0; X } X } X if ((sbuf->st_mode & S_IFMT) == S_IFLNK) { X if (link_ignore) X return 0; X } X X if ((sbuf->st_mode & S_IFMT) == S_IFDIR) X name = fn + strlen(head); X else X name = fn + strlen(head) + 1; X X if(strlen(name) == 0) X return 0; /* original dir call, ignore */ X X switch (ftype) { X default: X fprintf(out_fp, "echo Unknown"); X fprintf(out_fp, "\t%s", fn); X fprintf(out_fp, "\t%s", name); X fprintf(out_fp, "\n"); X break; X case FTW_F: X if ((sbuf->st_mode & S_IFMT) == S_IFLNK) { X fprintf(out_fp, "ln -s"); X fprintf(out_fp, "\t%s", fn); X fprintf(out_fp, "\t%s", name); X fprintf(out_fp, "\n"); X } else { X fprintf(out_fp, "ln -s "); X fprintf(out_fp, "\t%s", fn); X fprintf(out_fp, "\t%s", name); X fprintf(out_fp, "\n"); X } X if(debug)fprintf(stderr, "ln -s\t%s\t%s\n", fn, name); X break; X case FTW_D: X /* RCS is always a link to the real base system */ X /* have to look at the LAST 4 char of name for RCS directory */ X /* The RCS directory will be linked rather than mkdir'ed */ X temp = name + strlen(name) - 4; X if(strcmp(temp, "/RCS") == 0){ X /* if this link already exists, do NOT do it again, */ X /* an other link will lead to a loop in the master */ X /* tree. */ X char lbuf[BUFSIZ]; X if((status = readlink(name+1, lbuf, BUFSIZ)) >= 0) X return 0; X X sprintf(buf, "ln -s %s %s", fn, name + 1); X if(debug) X fprintf(stderr, "%s\n", buf); X X if ((sbuf->st_mode & S_IFMT) == S_IFDIR) { X struct stat xbuf; X X stat(fn, &xbuf); X /* if this is a link to a directory, ignore */ X if ((xbuf.st_mode & S_IFMT) == S_IFLNK) { X fprintf("%d:link to existing directory ignored:%s\n", X __FILE__, buf); X return 0; X } X } X } X else{ X char xbuf[512]; X strcpy(xbuf, name+1); X sprintf(buf, "mkdir %s", xbuf); X if(debug)fprintf(stderr, "%s\n", xbuf); X } X X#define FOR_REAL 1 X#if FOR_REAL X /* NEVER create a loop by linking with a file containing RCS/RCS */ X if(strsearch(buf, "RCS/RCS") == 0) X system(buf); X#else X fprintf(out_fp, "%s\n", buf); X#endif X break; X case FTW_NS: X fprintf(stderr, "stat_failed"); X fprintf(stderr, "\t%s", fn); X fprintf(stderr, "\t%s", name); X fprintf(stderr, "\n"); X break; X } X return 0; X} X Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X int save_argc; X int status; X char *prog_name; X char *s; X char buf[BUFSIZ]; X char sys_cmd[BUFSIZ]; X char *rcs_base_fn; X X /* parse the necessary options */ X save_argc = argc; X prog_name = *argv; X while (--argc > 0 && (*++argv)[0] == '-') { X for (s = argv[0] + 1; *s != '\0'; s++) { X switch (*s) { X default: X case '?': X fprintf(stderr, "usage: %s base_dir\n", prog_name); X fprintf(stderr, "\t-Ifile = use \"file\" as an include in fgrep\n"); X fprintf(stderr, "\t-Xfile = use \"file\" as an exlcude in fgrep\n"); X exit(1); X case 'd': X debug = 1; X break; X case 'I': X incl_fn = ++s; X while (*++s); /* to end of string */ X --s; /* but not too far */ X break; X case 'X': X excl_fn = ++s; X while (*++s); /* to end of string */ X --s; /* but not too far */ X break; X } X } X } X X /* compile regular expression to prevent descent into */ X /* an RCS linked subdirectory. */ X if(head = (char *)re_comp("\/RCS\/")){ X printf(head); X exit(1); X } X X temp_fn = (char *) mktemp("TTXXXXXX"); X out_fp = fopen(temp_fn, "w"); X X /* this may or may not exist */ X if(argc == 0) { X head = rcs_root_fn = (char *)getenv("ROOT_TREE"); X } X else X head = *argv; /* get the head */ X X X { X int i; X X /* move the environmental varialbe to a work area */ X for(i = 0; i < sizeof(head_buf); head_buf[i++] = '\0'); X strcpy(head_buf, head); X head = head_buf; X } X X /* prove existence/readability of RCS root file */ X if ((fp = fopen(rcs_root_fn, "r")) == (FILE *) 0) { X fprintf(stderr, "Cannot open $ROOT_TREE %s\n", rcs_base_fn); X exit(1); X } X fclose(fp); X X /* open the exclusion file, if none check under the ROOT_TREE dir */ X if ((excl_fp = fopen(excl_fn, "r")) == (FILE *) 0) { X sprintf(excl_fn = sys_cmd, "%s/excl_file", rcs_root_fn); X excl_fn = strdup(sys_cmd); /* save this string */ X if ((excl_fp = fopen(sys_cmd, "r")) == (FILE *) 0) { X fprintf(stderr, "Cannot read file exclusion list: %s\n", X excl_fn); X exit(1); X } X } X X /* open the inclusion file, if none check under the ROOT_TREE dir */ X if ((incl_fp = fopen(incl_fn, "r")) == (FILE *) 0) { X sprintf(sys_cmd, "%s/incl_file", rcs_root_fn); X incl_fn = strdup(sys_cmd); /* save this string */ X if ((incl_fp = fopen(sys_cmd, "r")) == (FILE *) 0) { X fprintf(stderr, "Cannot read file inclusion list: %s\n", X incl_fn); X exit(1); X } X } X X ftw(rcs_root_fn, print_file, 20); X X rewind(out_fp); X sprintf(sys_cmd, "cat %s", temp_fn); X if (incl_fp) { X sprintf(buf, "| fgrep -f %s ", incl_fn); X strcat(sys_cmd, buf); X } X if (excl_fp) { X sprintf(buf, "| fgrep -v -f %s ", excl_fn); X strcat(sys_cmd, buf); X } X strcat(sys_cmd, " | /bin/csh "); X /* Do not remove temp file if debugging */ X if(!debug) { X strcat(sys_cmd, "; rm "); X strcat(sys_cmd, temp_fn); X } X printf("%s\n", sys_cmd); X exit(system(sys_cmd)); X} END_OF_FILE echo shar: 1 control character may be missing from \"'lktree.c'\" if test 8287 -ne `wc -c <'lktree.c'`; then echo shar: \"'lktree.c'\" unpacked with wrong size! fi # end of 'lktree.c' fi echo shar: End of archive 1 \(of 1\). cp /dev/null ark1isdone MISSING="" for I in 1 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have the archive. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0