archx.c 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. /*
  2. * A R C H X
  3. *
  4. * Archive extraction
  5. *
  6. */
  7. /*
  8. * Note: the )BUILD comment is extracted by a Decus C tool to construct
  9. * system-dependent compiler command lines.
  10. *
  11. * Text inside #ifdef DOCUMENTATION is converted to runoff by a
  12. * Decus C tool.
  13. */
  14. /*)BUILD $(TKBOPTIONS) = {
  15. TASK = ...ARX
  16. }
  17. */
  18. #ifdef DOCUMENTATION
  19. title archx text file archiver extraction
  20. index text file archiver extraction
  21. synopsis
  22. arch archive_files
  23. description
  24. Archx manages archives (libraries) of source files, allowing
  25. a large number of small files to be stored without using
  26. excessive system resources. Archx extracts all files from
  27. an archive.
  28. If no archive_name file is given, the standard input is read.
  29. Archive header records are echoed to the standard output.
  30. archive file format
  31. Archive files are standard text files. Each archive element is
  32. preceeded by a line of the format:
  33. .s.nf
  34. -h- file.name date true_name
  35. .s.f
  36. Note that there is no line or byte count. To prevent problems,
  37. a '-' at the beginning of a record within a user file or embedded
  38. archive will be "quoted" by doubling it. The date and true filename
  39. fields are ignored. On some operating systems, file.name is
  40. forced to lowercase. The archive builder (archc) may prefix
  41. other characters by '-'.
  42. If the first non-blank line of an input file does not
  43. begin with "-h", the text will be appended to "archx.tmp"
  44. This is needed if archives are distributed by mail
  45. and arrive with initial routing and subject information.
  46. diagnostics
  47. Diagnostic messages should be self-explanatory
  48. author
  49. Martin Minow
  50. bugs
  51. #endif
  52. #include <stdio.h>
  53. #include <ctype.h>
  54. #define EOS 0
  55. #define FALSE 0
  56. #define TRUE 1
  57. #ifdef vms
  58. #include <ssdef.h>
  59. extern int errno;
  60. #define IO_ERROR errno
  61. #define IO_SUCCESS SS$_NORMAL
  62. #endif
  63. #ifndef IO_SUCCESS
  64. #define IO_SUCCESS 0
  65. #endif
  66. #ifndef IO_ERROR
  67. #define IO_ERROR 1
  68. #endif
  69. /*
  70. * The following status codes are returned by gethdr()
  71. */
  72. #define DONE 0
  73. #define GOTCHA 1
  74. #define NOGOOD 2
  75. char text[513]; /* Working text line */
  76. char name[81]; /* Current archive member name */
  77. char filename[81]; /* Working file name */
  78. char arfilename[81]; /* Archive file name */
  79. char fullname[81]; /* Output for argetname() */
  80. int verbose = TRUE; /* TRUE for verbosity */
  81. int first_archive; /* For mail header skipping */
  82. main(argc, argv)
  83. int argc; /* Arg count */
  84. char *argv[]; /* Arg vector */
  85. {
  86. register int i; /* Random counter */
  87. int status; /* Exit status */
  88. #ifdef vms
  89. argc = getredirection(argc, argv);
  90. #endif
  91. status = IO_SUCCESS;
  92. if (argc == 1)
  93. process();
  94. else {
  95. for (i = 1; i < argc; i++) {
  96. if (freopen(argv[i], "r", stdin) != NULL)
  97. process();
  98. else {
  99. perror(argv[i]);
  100. status = IO_ERROR;
  101. }
  102. }
  103. }
  104. exit(status);
  105. }
  106. process()
  107. /*
  108. * Process archive open on stdin
  109. */
  110. {
  111. register char *fn; /* File name pointer */
  112. register FILE *outfd;
  113. register int i;
  114. text[0] = EOS;
  115. while ((i = gethdr()) != DONE) {
  116. switch (i) {
  117. case GOTCHA:
  118. if ((outfd = fopen(name, "w")) == NULL) {
  119. perror(name);
  120. fprintf(stderr, "Can't create \"%s\"\n", name);
  121. arskip();
  122. continue;
  123. }
  124. break;
  125. case NOGOOD:
  126. fprintf(stderr, "Missing -h-, writing to archx.tmp\n");
  127. fprintf(stderr, "Current text line: %s", text);
  128. strcpy(name, "archx.tmp");
  129. if ((outfd = fopen(name, "a")) == NULL) {
  130. perror(name);
  131. fprintf(stderr, "Cannot append to %s\n", name);
  132. arskip();
  133. continue;
  134. }
  135. break;
  136. }
  137. arexport(outfd);
  138. fclose(outfd);
  139. }
  140. }
  141. int
  142. gethdr()
  143. /*
  144. * If text is null, read a record, returning to signal input state:
  145. * DONE Eof read
  146. * NOGOOD -h- wasn't first non-blank line. Line is in text[]
  147. * GOTCHA -h- found, parsed into name.
  148. */
  149. {
  150. register char *tp;
  151. register char *np;
  152. again: if (text[0] == EOS
  153. && fgets(text, sizeof text, stdin) == NULL)
  154. return (DONE);
  155. if (text[0] == '\n' && text[1] == EOS) {
  156. text[0] = EOS;
  157. goto again;
  158. }
  159. if (text[0] != '-'
  160. || text[1] != 'h'
  161. || text[2] != '-')
  162. return (NOGOOD);
  163. for (tp = &text[3]; isspace(*tp); tp++)
  164. ;
  165. for (np = name; !isspace(*tp); *np++ = *tp++)
  166. ;
  167. *np = EOS;
  168. return (GOTCHA);
  169. }
  170. arskip()
  171. /*
  172. * Skip to next header
  173. */
  174. {
  175. while (fgets(text, sizeof text, stdin) != NULL) {
  176. if (text[0] == '-' && text[1] == 'h' && text[2] == '-')
  177. return;
  178. }
  179. text[0] = EOS; /* EOF signal */
  180. }
  181. arexport(outfd)
  182. register FILE *outfd;
  183. /*
  184. * Read secret archive format, writing archived data to outfd.
  185. * Clean out extraneous <cr>,<lf>'s
  186. */
  187. {
  188. register char *tp;
  189. unsigned int nrecords;
  190. printf("Creating \"%s\", ", name);
  191. nrecords = 0;
  192. while (fgets(text, sizeof text, stdin) != NULL) {
  193. tp = &text[strlen(text)];
  194. if (tp > &text[1] && *--tp == '\n' && *--tp == '\r') {
  195. *tp++ = '\n';
  196. *tp = EOS;
  197. }
  198. if (text[0] == '-') {
  199. if (text[1] == 'h')
  200. goto gotcha;
  201. fputs(text+1, outfd);
  202. }
  203. else {
  204. fputs(text, outfd);
  205. }
  206. nrecords++;
  207. }
  208. text[0] = EOS;
  209. gotcha: printf("%u records\n", nrecords);
  210. if (ferror(stdin) || ferror(outfd))
  211. printf("Creation of \"%s\" completed with error\n", name);
  212. }
  213. /*
  214. * getredirection() is intended to aid in porting C programs
  215. * to VMS (Vax-11 C) which does not support '>' and '<'
  216. * I/O redirection. With suitable modification, it may
  217. * useful for other portability problems as well.
  218. */
  219. #ifdef vms
  220. static int
  221. getredirection(argc, argv)
  222. int argc;
  223. char **argv;
  224. /*
  225. * Process vms redirection arg's. Exit if any error is seen.
  226. * If getredirection() processes an argument, it is erased
  227. * from the vector. getredirection() returns a new argc value.
  228. *
  229. * Warning: do not try to simplify the code for vms. The code
  230. * presupposes that getredirection() is called before any data is
  231. * read from stdin or written to stdout.
  232. *
  233. * Normal usage is as follows:
  234. *
  235. * main(argc, argv)
  236. * int argc;
  237. * char *argv[];
  238. * {
  239. * argc = getredirection(argc, argv);
  240. * }
  241. */
  242. {
  243. register char *ap; /* Argument pointer */
  244. int i; /* argv[] index */
  245. int j; /* Output index */
  246. int file; /* File_descriptor */
  247. extern int errno; /* Last vms i/o error */
  248. for (j = i = 1; i < argc; i++) { /* Do all arguments */
  249. switch (*(ap = argv[i])) {
  250. case '<': /* <file */
  251. if (freopen(++ap, "r", stdin) == NULL) {
  252. perror(ap); /* Can't find file */
  253. exit(errno); /* Is a fatal error */
  254. }
  255. case '>': /* >file or >>file */
  256. if (*++ap == '>') { /* >>file */
  257. /*
  258. * If the file exists, and is writable by us,
  259. * call freopen to append to the file (using the
  260. * file's current attributes). Otherwise, create
  261. * a new file with "vanilla" attributes as if
  262. * the argument was given as ">filename".
  263. * access(name, 2) is TRUE if we can write on
  264. * the specified file.
  265. */
  266. if (access(++ap, 2) == 0) {
  267. if (freopen(ap, "a", stdout) != NULL)
  268. break; /* Exit case statement */
  269. perror(ap); /* Error, can't append */
  270. exit(errno); /* After access test */
  271. } /* If file accessable */
  272. }
  273. /*
  274. * On vms, we want to create the file using "standard"
  275. * record attributes. create(...) creates the file
  276. * using the caller's default protection mask and
  277. * "variable length, implied carriage return"
  278. * attributes. dup2() associates the file with stdout.
  279. */
  280. if ((file = creat(ap, 0, "rat=cr", "rfm=var")) == -1
  281. || dup2(file, fileno(stdout)) == -1) {
  282. perror(ap); /* Can't create file */
  283. exit(errno); /* is a fatal error */
  284. } /* If '>' creation */
  285. break; /* Exit case test */
  286. default:
  287. argv[j++] = ap; /* Not a redirector */
  288. break; /* Exit case test */
  289. }
  290. } /* For all arguments */
  291. return (j);
  292. }
  293. #endif