static char SccsId
[] = "@(#)sccs.c 1.23 %G%";
# define bitset(bit, word) ((bit) & (word))
char *sccsname
; /* name of SCCS routine */
short sccsoper
; /* opcode, see below */
short sccsflags
; /* flags, see below */
char *sccspath
; /* pathname of binary implementing */
/* values for sccsoper */
# define PROG 0 /* call a program */
# define CMACRO 1 /* command substitution macro */
# define FIX 2 /* fix a delta */
# define CLEAN 3 /* clean out recreatable files */
# define UNEDIT 4 /* unedit a file */
# define NO_SDOT 0001 /* no s. on front of args */
# define REALUSER 0002 /* protected (e.g., admin) */
# define PROGPATH(name) "/usr/local/name"
# define PROGPATH(name) "/usr/sccs/name"
struct sccsprog SccsProg
[] =
"admin", PROG
, REALUSER
, PROGPATH(admin
),
"chghist", PROG
, 0, PROGPATH(rmdel
),
"comb", PROG
, 0, PROGPATH(comb
),
"delta", PROG
, 0, PROGPATH(delta
),
"get", PROG
, 0, PROGPATH(get
),
"help", PROG
, NO_SDOT
, PROGPATH(help
),
"prt", PROG
, 0, PROGPATH(prt
),
"rmdel", PROG
, REALUSER
, PROGPATH(rmdel
),
"what", PROG
, NO_SDOT
, PROGPATH(what
),
"edit", CMACRO
, 0, "get -e",
"delget", CMACRO
, 0, "delta/get",
"deledit", CMACRO
, 0, "delta/get -e",
"del", CMACRO
, 0, "delta/get",
"delt", CMACRO
, 0, "delta/get",
"clean", CLEAN
, REALUSER
, (char *) TRUE
,
"info", CLEAN
, REALUSER
, (char *) FALSE
,
"unedit", UNEDIT
, 0, NULL
,
char *p_osid
; /* old SID */
char *p_nsid
; /* new SID */
char *p_user
; /* user who did edit */
char *p_date
; /* date of get */
char *p_time
; /* time of get */
char *SccsPath
= "SCCS"; /* pathname of SCCS files */
bool RealUser
; /* if set, running as real user */
bool Debug
; /* turn on tracing */
extern struct sccsprog
*lookup();
** Detect and decode flags intended for this program.
fprintf(stderr
, "Usage: sccs [flags] command [flags]\n");
if (lookup(argv
[0]) == NULL
)
while ((p
= *++argv
) != NULL
)
case 'r': /* run as real user */
case 'p': /* path of sccs files */
fprintf(stderr
, "Sccs: unknown option -%s\n", p
);
register struct sccsprog
*cmd
;
extern struct sccsprog
*lookup();
for (avp
= argv
; *avp
!= NULL
; avp
++)
printf(" \"%s\"\n", *avp
);
** At this point, argv points to the command name.
fprintf(stderr
, "Sccs: Unknown command \"%s\"\n", argv
[0]);
** Interpret operation associated with this command.
case PROG
: /* call an sccs prog */
callprog(cmd
->sccspath
, cmd
->sccsflags
, argv
, forkflag
);
case CMACRO
: /* command macro */
for (p
= cmd
->sccspath
; *p
!= '\0'; p
++)
for (q
= buf
; *p
!= '/' && *p
!= '\0'; p
++, q
++)
xcommand(&argv
[1], *p
!= '\0', nav
[0], nav
[1], nav
[2],
nav
[3], nav
[4], nav
[5], nav
[6]);
fprintf(stderr
, "Sccs internal error: CMACRO\n");
case FIX
: /* fix a delta */
if (strncmp(argv
[1], "-r", 2) != 0)
fprintf(stderr
, "Sccs: -r flag needed for fix command\n");
xcommand(&argv
[1], TRUE
, "get", "-k", NULL
);
xcommand(&argv
[1], TRUE
, "rmdel", NULL
);
xcommand(&argv
[2], FALSE
, "get", "-e", "-g", NULL
);
fprintf(stderr
, "Sccs internal error: FIX\n");
clean((bool) cmd
->sccspath
);
for (avp
= &argv
[1]; *avp
!= NULL
; avp
++)
xcommand(nav
, FALSE
, "get", NULL
);
fprintf(stderr
, "Sccs internal error: oper %d\n", cmd
->sccsoper
);
** LOOKUP -- look up an SCCS command name.
** name -- the name of the command to look up.
** ptr to command descriptor for this command.
** NULL if no such entry.
register struct sccsprog
*cmd
;
for (cmd
= SccsProg
; cmd
->sccsname
!= NULL
; cmd
++)
if (strcmp(cmd
->sccsname
, name
) == 0)
xcommand(argv
, forkflag
, arg0
)
for (av
= &arg0
; *av
!= NULL
; av
++)
for (av
= argv
; *av
!= NULL
; av
++)
command(newargv
, forkflag
);
callprog(progpath
, flags
, argv
, forkflag
)
fprintf(stderr
, "Sccs: cannot fork");
** Build new argument vector.
/* copy program filename arguments and flags */
while ((p
= *++av
) != NULL
)
if (!bitset(NO_SDOT
, flags
) && *p
!= '-')
** Set protection as appropriate.
if (bitset(REALUSER
, flags
))
** Call real SCCS program.
fprintf(stderr
, "Sccs: cannot execute ");
** MAKEFILE -- make filename of SCCS file
** If the name passed is already the name of an SCCS file,
** just return it. Otherwise, munge the name into the name
** of the actual SCCS file.
** There are cases when it is not clear what you want to
** do. For example, if SccsPath is an absolute pathname
** and the name given is also an absolute pathname, we go
** for SccsPath (& only use the last component of the name
** passed) -- this is important for security reasons (if
** sccs is being used as a setuid front end), but not
** particularly intuitive.
** name -- the file name to be munged.
** The pathname of the sccs file.
** Check to see that the path is "safe", i.e., that we
** are not letting some nasty person use the setuid part
** of this program to look at or munge some presumably
if (SccsPath
[0] == '/' && !safepath(name
))
** Create the eventual pathname.
if (strncmp(p
, "s.", 2) != 0)
strncat(buf
, name
, p
- name
);
if (stat(buf
, &stbuf
) >= 0 && (stbuf
.st_mode
& S_IFMT
) == S_IFDIR
)
p
= malloc(strlen(buf
) + 1);
** SAFEPATH -- determine whether a pathname is "safe"
** "Safe" pathnames only allow you to get deeper into the
** directory structure, i.e., full pathnames and ".." are
** p -- the name to check.
** TRUE -- if the path is safe.
** FALSE -- if the path is not safe.
** Prints a message if the path is not safe.
while (strncmp(p
, "../", 3) != 0 && strcmp(p
, "..") != 0)
printf("You may not use full pathnames or \"..\"\n");
** CLEAN -- clean out recreatable files
** Any file for which an "s." file exists but no "p." file
** exists in the current directory is purged.
** really -- if TRUE, remove everything.
** else, just report status.
** removes files in the current directory.
dirfd
= fopen(SccsPath
, "r");
fprintf(stderr
, "Sccs: cannot open %s\n", SccsPath
);
** Scan the SCCS directory looking for s. files.
while (fread(&dir
, sizeof dir
, 1, dirfd
) != NULL
)
if (dir
.d_ino
== 0 || strncmp(dir
.d_name
, "s.", 2) != 0)
/* got an s. file -- see if the p. file exists */
basefile
= &buf
[strlen(buf
)];
strncpy(basefile
, &dir
.d_name
[2], sizeof dir
.d_name
- 2);
basefile
[sizeof dir
.d_name
- 2] = '\0';
while (fgets(pline
, sizeof pline
, pfp
) != NULL
)
printf("%12s: being edited: %s", basefile
, pline
);
/* the s. file exists and no p. file exists -- unlink the g-file */
strncpy(buf
, &dir
.d_name
[2], sizeof dir
.d_name
- 2);
buf
[sizeof dir
.d_name
- 2] = '\0';
printf("Nothing being edited\n");
** UNEDIT -- unedit a file
** Checks to see that the current user is actually editting
** the file and arranges that s/he is not editting it.
** fn -- the name of the file to be unedited.
** TRUE -- if the file was successfully unedited.
** FALSE -- if the file was not unedited for some
** entries are removed from pfile.
static char tfn
[] = "/tmp/sccsXXXXX";
extern struct pfile
*getpfile();
/* make "s." filename & find the trailing component */
if (q
[1] != 's' || q
[2] != '.')
fprintf(stderr
, "Sccs: bad file name \"%s\"\n", fn
);
/* turn "s." into "p." */
printf("%12s: not being edited\n", fn
);
** Copy p-file to temp file, doing deletions as needed.
fprintf(stderr
, "Sccs: cannot create \"%s\"\n", tfn
);
while ((pent
= getpfile(pfp
)) != NULL
)
if (strcmp(pent
->p_user
, myname
) == 0)
fprintf(tfp
, "%s %s %s %s %s\n", pent
->p_osid
,
pent
->p_nsid
, pent
->p_user
, pent
->p_date
,
if (freopen(tfn
, "r", tfp
) == NULL
)
fprintf(stderr
, "Sccs: cannot reopen \"%s\"\n", tfn
);
if (freopen(pfn
, "w", pfp
) == NULL
)
fprintf(stderr
, "Sccs: cannot create \"%s\"\n", pfn
);
while (fgets(buf
, sizeof buf
, tfp
) != NULL
)
printf("%12s: removed\n", fn
);
printf("%12s: not being edited by you\n", fn
);
** GETPFILE -- get an entry from the p-file
** pfp -- p-file file pointer
** pointer to p-file struct for next entry
** Each call wipes out results of previous call.
extern char *nextfield();
if (fgets(buf
, sizeof buf
, pfp
) == NULL
)
ent
.p_nsid
= p
= nextfield(p
);
ent
.p_user
= p
= nextfield(p
);
ent
.p_date
= p
= nextfield(p
);
ent
.p_time
= p
= nextfield(p
);
if (p
== NULL
|| nextfield(p
) != NULL
)
if (p
== NULL
|| *p
== '\0')
while (*p
!= ' ' && *p
!= '\n' && *p
!= '\0')
if (*p
== '\n' || *p
== '\0')