/* $Id: shar.c,v 2.4 2011/02/23 17:24:37 ksb Exp $ */ #include #include #include #include /* for MAXHOSTNAMELEN under Solaris */ #include #include #include #include #include #include #include #include #include #include #include #include "machine.h" #include "shar.h" #include "tree.h" #include "main.h" static char acPartName[MAXPATHLEN + 1]; static char acAllDoneText[100]; static FILE *fp; char *apcHeader[] = { "#! /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 pw_name; } else { pcWho = "unknown"; } (void)strncpy(acWhoami, pcWho, 9); (void)strcat(acWhoami, "@"); gethostname(acWhoami + strlen(acWhoami), MAXHOSTNAMELEN); (void)strcat(acWhoami, " on "); (void)strcat(acWhoami, ctime(&now)); return acWhoami; } /* start writing a shar file, which will wrapper a set of chunks (bj) * created with SharMakeChunk. */ void SharStart() { register char **ppc; if ((char *)0 != pcOutputDir && '\000' != pcOutputDir[0]) { /* it's a multipart, create a new part */ sprintf(acPartName, "%s/part%02d", pcOutputDir, iElementNumber); if ((FILE *)0 == (fp = fopen(acPartName, "w"))) { fprintf(stderr, "%s: open: %s: %s\n", progname, acPartName, strerror(errno)); exit(EX_CANTCREAT); } } else { fp = pfOutputFile; } /* our done message is different if we're multipart */ if (0 != iTotalElements) { sprintf(acAllDoneText, "End of part %d (of %d).", iElementNumber, iTotalElements); } else { sprintf(acAllDoneText, "End of shell archive."); } for (ppc = apcHeader; *ppc; ppc++) { fprintf(fp, "%s\n", *ppc); } fprintf(fp, "# \"%s\"\n", acAllDoneText); /* TABLE OF CONTENTS */ fprintf(fp, "# created by %s", SharWhoami()); fputs("# using nushar, by Ben Jackson \nPATH=/usr/5bin:/bin:/usr/bin:/usr/bsd:/usr/ucb:/usr/etc ; export PATH\n", fp); if (fExtractFancy) { fputs("touch 2>&1 | fgrep 'amc' > /dev/null && TOUCH=touch || TOUCH=true\n", fp); } } static void _SharAddDirs(pcName) char *pcName; { if ('\000' == pcName[0]) { return; } fprintf(fp, "[ ! -d '%s' ] && echo Creating dir \\\"'%s'\\\" && mkdir '%s'\n", pcName, pcName, pcName); } /* this is pretty ugly. something has marked a set of dirs (bj) * from the files they're just about to add to this shar file, * and now wants to add commands to create those dirs to the shar file. */ void SharAddDirs() { UseDirMarks(_SharAddDirs); } void SharAddChunk(pcName) char *pcName; { static char acBuf[8192]; register int fd; register int iLength, iRet; if ((fd = open(pcName, O_RDONLY)) < 0) { fprintf(stderr, "%s: open: %s: %s\n", progname, pcName, strerror(errno)); exit(EX_CANTCREAT); } while ((iLength = read(fd, acBuf, sizeof(acBuf))) > 0) { if (iLength != (iRet = fwrite(acBuf, 1, iLength, fp))) { fprintf(stderr, "%s: fwrite: %d elems: %s\n", progname, iRet, strerror(errno)); exit(EX_OSERR); } } if (-1 == iLength) { fprintf(stderr, "%s: read: %s: %s\n", progname, pcName, strerror(errno)); exit(EX_IOERR); } close(fd); unlink(pcName); } /* build a file which is a piece of a shell script capable of (bj) * extracting the file in pcRealFilename */ char * SharMakeChunk(pcRealFilename, piSize, psBuf) char *pcRealFilename; long int *piSize; struct stat *psBuf; { static int iCounter = 0; char fUUencoded = 0; char acTmpFile[MAXPATHLEN+1]; char *pcFilename = pcRealFilename; char *pcResult; struct tm *ptm; int iTime; int iMode = psBuf->st_mode; int iSize = psBuf->st_size; FILE *fp; /* get a 10 digit "touch" style representation of the mtime */ ptm = localtime(&psBuf->st_mtime); iTime = ptm->tm_year + 100 * ptm->tm_min + 10000 * ptm->tm_hour + 1000000 * (ptm->tm_mday) + 100000000 * (ptm->tm_mon + 1); /* if we don't want the dir tree, and the current thing is a * dir or a link to a dir, skip it. * otherwise, just strip the path off the name. */ if (fNoDirTree) { struct stat statbuf; char *s = pcRealFilename; if (S_ISDIR(iMode)) return NULL; /* are we a link to a directory? */ if (S_ISLNK(iMode)) { if (stat(pcRealFilename, &statbuf) < 0) { fprintf(stderr, "%s: stat: %s: %s\n", progname, pcRealFilename, strerror(errno)); exit(EX_NOINPUT); } if (S_ISDIR(statbuf.st_mode)) return NULL; } while ('\000' != *s) { if ('/' == *s++) pcFilename = s; } } /* make a temporary file name for this shar chunk * ZZZ */ sprintf(acTmpFile, "/tmp/%s%03x%04x", progname, iCounter++, getpid()); pcResult = (char *) strdup(acTmpFile); if ((FILE *)0 == (fp = fopen(acTmpFile, "w"))) { fprintf(stderr, "%s: fopen: %s: %s\n", progname, acTmpFile, strerror(errno)); return NULL; } /* character and block special files, mknod name [c|b] maj min * this is why we have /usr/etc in our path */ if (S_ISCHR(iMode) || S_ISBLK(iMode)) { char cType = S_ISCHR(iMode) ? 'c' : 'b'; fprintf(fp, "[ ! -%c '%s' ] && echo shar: Creating %s special device \\\"'%s'\\\" && { mknod '%s' %c %ld %ld || echo shar: Could not create \\\"'%s'\\\"; }\n", cType, pcFilename, ('c' == cType) ? "character" : "block", pcFilename, pcFilename, cType, (long)((psBuf->st_rdev >> 8) & 0xff), (long)(psBuf->st_rdev & 0xff), pcFilename); goto DONE; } /* fifos (named pipes), mknod name p */ if (S_ISFIFO(iMode)) { fprintf(fp, "[ ! -p '%s' ] && echo shar: Creating FIFO \\\"'%s'\\\" && mknod '%s' p || echo shar: Could not create \\\"'%s'\\\"\n", pcFilename, pcFilename, pcFilename, pcFilename); goto DONE; } /* sockets can't be made with (standard) user level commands */ if (S_ISSOCK(iMode)) { fprintf(stderr, "%s: %s: socket not packed (warning left in shar file)\n", progname, pcFilename); fprintf(fp, "echo shar: \\\"'%s'\\\" was a socket (not packed)\n", pcFilename); goto DONE; } if (S_ISLNK(iMode)) { char acBuf[MAXPATHLEN+1]; char *pcLinkname = acBuf; if (readlink(pcRealFilename, acBuf, MAXPATHLEN) < 0) { fprintf(stderr, "%s: readlink: %s: %s\n", progname, pcRealFilename, strerror(errno)); exit(EX_IOERR); } if (fNoDirTree) { char *s = acBuf; while (*s) { if ('/' == *s++) pcLinkname = s; } } fprintf(fp, "if test ! -h '%s' ; then\n\techo shar: Creating symlink \\\"'%s'\\\"\n\tln -s '%s' '%s'\nfi\n", pcFilename, pcFilename, pcLinkname, pcFilename); goto DONE; } if (S_ISREG(iMode)) { FILE *pfIn; long iExtractOffset; int c, cLast; /* the clobber testing */ fprintf(fp, "if test -f '%s' -a \"${1}\" != \"-c\" ; then\n\techo shar: Not clobbering existing file \\\"'%s'\\\"\nelse\n\techo shar: Extracting \\\"'%s'\\\" \\(%d bytes\\)\n", pcFilename, pcFilename, pcFilename, iSize); /* remember where we are if we need to go back */ iExtractOffset = ftell(fp); if (pcPrefix && pcPrefix[0]) { fprintf(fp, "\tsed -e 's/^%s//' >'%s' <<'%s'%s\n", pcPrefix, pcFilename, pcDelimiter, fExtractFancy ? " &&" : ""); } else { fprintf(fp, "\tcat >'%s' <<'%s'%s\n", pcFilename, pcDelimiter, fExtractFancy ? " &&" : ""); } /* insert the file */ if ((FILE *)0 == (pfIn = fopen(pcRealFilename, "r"))) { fprintf(stderr, "%s: fopen: %s: %s\n", progname, pcRealFilename, strerror(errno)); return NULL; } cLast = '\n'; while (EOF != (c = getc(pfIn))) { if ('\n' == cLast && pcPrefix && pcPrefix[0]) fputs(pcPrefix, fp); if (!(isprint(c) || isspace(c))) break; putc(c, fp); cLast = c; } fclose(pfIn); /* oh no! binary data! sound the alarm! * chop off what we had, popen uuencode and spit that out in * place of what we have. */ if (EOF != c) { char acCommand[MAXPATHLEN*3]; char acBuf[8192]; int iLength; fUUencoded = 1; sprintf(acCommand, "uuencode '%s' '%s'", pcRealFilename, pcFilename); if ((FILE *)0 == (pfIn = popen(acCommand, "r"))) { fprintf(stderr, "%s: popen(%s, \"r\"): %s\n", progname, acCommand, strerror(errno)); exit(EX_SOFTWARE); } fseek(fp, iExtractOffset, SEEK_SET); fprintf(fp, "\tuudecode <<'%s'%s\n", pcDelimiter, fExtractFancy ? " &&" : ""); while ((iLength = fread(acBuf, 1, sizeof(acBuf), pfIn)) > 0) { if (iLength != fwrite(acBuf, 1, iLength, fp)) { fprintf(stderr, "%s: fwrite: %s\n", progname, strerror(errno)); exit(EX_OSERR); } } /* uuencode exited nonzero, printed it's own error */ if (pclose(pfIn)) { exit(EX_SOFTWARE); } } else if ('\n' != cLast) { /* gotta make sure it ends in a newline, otherwise * the here-document-end token won't start a line * ZZZ fix the size here for the wc below? */ putc('\n', fp); } fprintf(fp, "%s\n", pcDelimiter); /* set the time and mode. * if we were uuencoded, the mode is already done. */ if (fExtractFancy) { putc('\t', fp); if (!fUUencoded) fprintf(fp, "chmod %04o '%s' && ", iMode &07777, pcFilename); fprintf(fp, "$TOUCH -amf %010d '%s' ||\n\techo shar: attempt to extract \\\"'%s'\\\" failed.\n", iTime, pcFilename, pcFilename); } if (fCheckSizes) { fprintf(fp, "\tif test %d -ne \"`wc -c < '%s'`\" ; then\n\t\techo shar: error extracting \\\"'%s'\\\", \\(should have been %d bytes\\)\n\tfi\n", iSize, pcFilename, pcFilename, iSize); } fputs("fi\n", fp); /* the clobber test */ goto DONE; } /* if success we were jumped over--into DONE below * we were unable to figure out what the file was. */ fprintf(stderr, "%s: %s: don't know what file mode 0x%08x means!\n", progname, pcFilename, iMode); free(pcResult); fclose(fp); unlink(acTmpFile); return NULL; DONE: fflush(fp); *piSize = ftell(fp); fprintf(stderr, "%7ld %s\n", *piSize, pcRealFilename); fclose(fp); return pcResult; } char *apcFooter[] = { "# End of shell archive.", "exit 0", NULL }; void SharEnd() { int i; char **ppc; fprintf(fp, "echo '%s'\n", acAllDoneText); /* if we're multipart, mark our part, test for the others, * test for completion. */ if (iTotalElements) { fprintf(fp, "echo %02d >> _shar_private\nsort -n -u _shar_private | {\nMISSING=\"\"\nread j\n", iElementNumber); fputs("for i in ", fp); for (i = 1; i <= iTotalElements; ++i) { fprintf(fp, "%02d ", i); } fputs("; do\n\tif test $i = \"$j\" ; then\n\t\tread j\n\telse\n\t\tMISSING=\"$MISSING $i\"\n\tfi\ndone\n", fp); fputs("if test -z \"${MISSING}\" ; then\n\techo Finished extracting all parts!\n\trm -f _shar_private\n", fp); } if ((char *)0 != pcInstructions && '\000' != *pcInstructions) { register char *s; fputs(iTotalElements ? "\techo \"":"echo \"", fp); for (s = pcInstructions; *s; s++) { switch (*s) { case '"': fputs("\\\"", fp); break; case '`': fputs("\\`", fp); break; default: putc(*s, fp); break; } } fputs("\"\n", fp); } if (iTotalElements) { fputs("else\n\techo You still need to extract the following parts:\n\techo \" ${MISSING}\"\nfi\n}\n", fp); } for (ppc = apcFooter; *ppc; ppc++) fprintf(fp, "%s\n", *ppc); if (fp != stdout) fclose(fp); } int SharOverhead() { return 1500; }