├── .gitignore ├── COPYING ├── Makefile ├── dumper.h ├── read20.c ├── t10backup.c ├── t10backup.h ├── tapecopy.c ├── tapedump.c ├── tapeio.c ├── tapeio.h ├── taperead.c ├── tapewrite.c └── tapex.c /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.o 3 | *.d 4 | tapecopy 5 | tapedump 6 | taperead 7 | tapewrite 8 | read20 9 | t10backup 10 | tapex 11 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 675 Mass Ave, Cambridge, MA 02139, USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 19yy 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) 19yy name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Library General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for tapeutils 2 | # Copyright 1998, 1999, 2000 Eric Smith 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License version 2 as published 6 | # by the Free Software Foundation. Note that permission is not granted 7 | # to redistribute this program under the terms of any later version of the 8 | # General Public License. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | # ----------------------------------------------------------------------------- 19 | # options 20 | # ----------------------------------------------------------------------------- 21 | 22 | UNAME != uname 23 | 24 | # CFLAGS = -O2 -Wall 25 | # LDFLAGS = 26 | 27 | CFLAGS = -g -Wall 28 | LDFLAGS = -g 29 | 30 | ifeq ($(UNAME),FreeBSD) 31 | LIBS=-lcompat 32 | endif 33 | 34 | # ----------------------------------------------------------------------------- 35 | # You shouldn't have to change anything below this point, but if you do please 36 | # let me know why so I can improve this Makefile. 37 | # ----------------------------------------------------------------------------- 38 | 39 | PACKAGE = tapeutils 40 | VERSION = 0.6 41 | DSTNAME = $(PACKAGE)-$(VERSION) 42 | 43 | PROGRAMS = tapecopy tapedump taperead tapewrite t10backup read20 tapex 44 | 45 | HEADERS = tapeio.h t10backup.h dumper.h 46 | SOURCES = tapeio.c tapecopy.c tapedump.c taperead.c tapewrite.c t10backup.c read20.c tapex.c 47 | MISC = COPYING 48 | 49 | DISTFILES = $(MISC) Makefile $(HEADERS) $(SOURCES) 50 | 51 | all: $(PROGRAMS) $(MISC_TARGETS) 52 | 53 | dist: $(DISTFILES) 54 | -rm -rf $(DSTNAME) 55 | mkdir $(DSTNAME) 56 | for f in $(DISTFILES); do ln $$f $(DSTNAME)/$$f; done 57 | tar --gzip -chf $(DSTNAME).tar.gz $(DSTNAME) 58 | -rm -rf $(DSTNAME) 59 | 60 | clean: 61 | rm -f $(PROGRAMS) $(MISC_TARGETS) *.o 62 | 63 | 64 | tapecopy: tapecopy.o tapeio.o $(LIBS) 65 | 66 | tapedump: tapedump.o tapeio.o $(LIBS) 67 | 68 | taperead: taperead.o tapeio.o $(LIBS) 69 | 70 | tapewrite: tapewrite.o tapeio.o $(LIBS) 71 | 72 | t10backup: t10backup.o tapeio.o $(LIBS) 73 | 74 | read20: read20.o tapeio.o $(LIBS) 75 | 76 | tapex: tapex.o tapeio.o $(LIBS) 77 | 78 | 79 | include $(SOURCES:.c=.d) 80 | 81 | %.d: %.c 82 | $(CC) -M -MG $(CFLAGS) $< | sed -e 's@ /[^ ]*@@g' -e 's@^\(.*\)\.o:@\1.d \1.o:@' > $@ 83 | -------------------------------------------------------------------------------- /dumper.h: -------------------------------------------------------------------------------- 1 | /* 5 bytes per 36-bit word */ 2 | /* 518 word logical blocks */ 3 | #define TAPEBLK 518*5*15 4 | 5 | /* Checksum is first word */ 6 | #define WdoffChecksum 0 7 | #define BtoffChecksum 0 8 | #define BtlenChecksum 36 9 | /* Page access bits is second word */ 10 | #define WdoffAccess 1 11 | #define BtoffAccess 0 12 | #define BtlenAccess 36 13 | /* SCD, first 3 bits in next word */ 14 | #define WdoffSCD 2 15 | #define BtoffSCD 0 16 | #define BtlenSCD 3 17 | /* Number of saveset on tape */ 18 | #define WdoffSaveSetNum 2 19 | #define BtoffSaveSetNum 3 20 | #define BtlenSaveSetNum 15 21 | /* Tape number of dump */ 22 | #define WdoffTapeNum 2 23 | #define BtoffTapeNum 18 24 | #define BtlenTapeNum 18 25 | /* F1, F2 Flag bits */ 26 | #define WdoffF1F2 3 27 | #define BtoffF1F2 0 28 | #define BtlenF1F2 2 29 | /* File Number in Set (new format only) */ 30 | #define WdoffFileNum 3 31 | #define BtoffFileNum 2 32 | #define BtlenFileNum 16 33 | /* Page Number in file */ 34 | #define WdoffPageNum 3 35 | #define BtoffPageNum 18 36 | #define BtlenPageNum 18 37 | /* Record type (2's complement) */ 38 | #define WdoffRectype 4 39 | #define BtoffRectype 0 40 | #define BtlenRectype 36 41 | /* Record sequence number */ 42 | #define WdoffRecseq 5 43 | #define BtoffRecseq 0 44 | #define BtlenRecseq 36 45 | 46 | 47 | /* SCD Values */ 48 | #define SCDNormal 0 49 | #define SCDCollection 1 50 | #define SCDArchive 2 51 | #define SCDMigration 3 52 | 53 | /* F1, F2 Values */ 54 | #define F1F2Old 0 55 | #define F1F2OldContinue 3 56 | #define F1F2New 1 57 | #define F1F2NewContinue 2 58 | 59 | /* Record type values */ 60 | #define RectypeData 0 61 | #define RectypeTphd 1 62 | #define RectypeFlhd 2 63 | #define RectypeFltr 3 64 | #define RectypeTptr 4 65 | #define RectypeUsr 5 66 | #define RectypeCtph 6 67 | #define RectypeFill 7 68 | 69 | char *rectypes[] = { 70 | "DATA", 71 | "ISSH", 72 | "FLHD", 73 | "FLTR", 74 | "TPTR", 75 | "UDIR", 76 | "CSSH", 77 | "FILL", 78 | }; 79 | 80 | #define BtoffWord 0 81 | #define BtlenWord 36 82 | 83 | #define WdoffSSstart 6 /* Start of saveset data */ 84 | #define WdoffSSFmt 6 /* Format of tape */ 85 | #define WdoffSSPtr 7 /* Pointer to saveset name (or 3 or 020 depending on format) */ 86 | #define WdoffSSDate 8 /* Saveset date offset (type 1, 6) */ 87 | #define WdoffSSName 9 /* Saveset name offset (type 1, 6) */ 88 | #define WdoffFLName 6 /* Filename offset (type 2) */ 89 | #define WdoffSSMsg (020+WdoffSSstart) /* Saveset name (unless SSPtr set) */ 90 | #define WdoffFDB 134 /* FDB offset (type 2) */ 91 | 92 | #define WdoffFDB_CTL 01+WdoffFDB /* Control word .FBCTL */ 93 | 94 | #define BtoffFDB_Arc 11 /* archived */ 95 | #define BtlenFDB_Arc 1 96 | 97 | #define BtoffFDB_Inv 12 /* invisible */ 98 | #define BtlenFDB_Inv 1 99 | 100 | #define BtoffFDB_Off 13 /* offline */ 101 | #define BtlenFDB_Off 1 102 | 103 | #define WdoffFDB_PRT 04+WdoffFDB /* protection */ 104 | #define BtoffFDB_PRT 18 105 | #define BtlenFDB_PRT 18 106 | 107 | #define WdoffFDB_BSZ 011+WdoffFDB /* Number of bits per byte */ 108 | #define BtoffFDB_BSZ 6 109 | #define BtlenFDB_BSZ 6 110 | 111 | #define WdoffFDB_PGC 011+WdoffFDB /* Number of pages in the file */ 112 | #define BtoffFDB_PGC 18 113 | #define BtlenFDB_PGC 18 114 | 115 | #define WdoffFDB_Size 012+WdoffFDB /* Number of bytes in the file */ 116 | 117 | #define BtoffFDB_Size 0 118 | #define BtlenFDB_Size 36 119 | 120 | #define WdoffFDB_Wrt 014+WdoffFDB /* Date of last write to file */ 121 | 122 | #define WdoffFDB_Ref 015+WdoffFDB /* read time */ 123 | 124 | #define WdoffFDB_PGC_A 022+WdoffFDB /* Pagecount before archive */ 125 | 126 | #define WdoffFDB_TP1 033+WdoffFDB /* Tape ID for archive run 1 */ 127 | 128 | #define WdoffFDB_SS1 034+WdoffFDB /* Saveset # for archive run 1 */ 129 | #define BtoffFDB_SS 0 130 | #define BtlenFDB_SS 18 131 | #define WdoffFDB_TF1 034+WdoffFDB /* Tape file # for archive run 1 */ 132 | #define BtoffFDB_TF 18 133 | #define BtlenFDB_TF 18 134 | 135 | #define WdoffFDB_TP2 035+WdoffFDB /* Tape ID for archive run 2 */ 136 | #define WdoffFDB_SS2 036+WdoffFDB /* Saveset # for archive run 2 */ 137 | #define WdoffFDB_TF2 036+WdoffFDB /* Tape file # for archive run 2 */ 138 | -------------------------------------------------------------------------------- /read20.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Program to read Tops-20 Dumper format tapes 3 | * 4 | * Jim Guyton, Rand Corporation 5 | * Original 10/20/82 6 | * jdg: -n added 6/11/83 7 | * jdg: can now extract 8-bit-byte files 2/9/86 8 | * 9 | * Lot of mods by Jay Lepreau, Univ of Utah, 1-2/87. 10 | * See the RCS log for details. 11 | * 12 | * Modified by Eric Smith , 4-DEC-2000, to use 13 | * tapeio library, in order to use tape image files. Also changed 14 | * to use environment variable TAPE if set, otherwise default to /dev/nst0. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #define _REGEX_RE_COMP 31 | #include 32 | 33 | #include "dumper.h" 34 | #include "tapeio.h" 35 | 36 | #define LOGFILE "Logfile" /* logfile should be changeable */ 37 | 38 | 39 | tape_handle_t tape_handle; 40 | 41 | char tapeblocka[TAPEBLK]; /* One logical record from tape */ 42 | FILE *fpFile; /* Output file handle on extracts */ 43 | int debug = 0; 44 | int textflg = 0; /* Non-zero if retr binary files as text */ 45 | int binflg = 0; /* Non-zero if retr all files */ 46 | int numflg = 0; /* Non-zero if using numeric filenames */ 47 | int keepcr = 0; /* Keep CR's in CRLF pairs in text files */ 48 | int dodir = 0; /* directory listing */ 49 | int xflg = 0; /* extract */ 50 | int verbose = 0; 51 | int genflg; /* keep generation number */ 52 | int nselect; /* number of files still to be selected by number */ 53 | int doallflag; /* act on all files cause no args given */ 54 | int label; /* there was a tape label */ 55 | 56 | int number; /* Current output file "number" */ 57 | 58 | #define TAPE "/dev/nst0" /* Default input tape */ 59 | 60 | int bytesize; /* Number of bits/byte in current file */ 61 | long numbytes; /* Number of bytes in current file */ 62 | long truncate_length = 0; /* For -b, truncate output to this. */ 63 | int pgcount; /* Number of twenex pages in file */ 64 | long pageno, tapeno, ssno, filenum; 65 | 66 | unsigned tprot; /* Tops-20 protection */ 67 | char *timeptr; 68 | 69 | struct utimbuf timep; 70 | 71 | int offline, archived, invisible; 72 | int apgcount, tp1, tp2, ss1, ss2, tf1, tf2; 73 | 74 | char topsname[130]; 75 | char sunixname[300]; 76 | 77 | struct want { 78 | unsigned short ssnum; 79 | unsigned short fnum; 80 | } want[10000]; /* limited by 20000 char arglist */ 81 | 82 | int cursswant; 83 | 84 | char **patterns = 0; /* Filename match patterns */ 85 | int numpats = 0; /* Number of patterns */ 86 | char *expression = 0; 87 | char *re_comp_error; /* Error message from re_comp() */ 88 | extern char *re_comp(); 89 | 90 | #if defined(__APPLE__) || defined(__OpenBSD__) || defined(__CYGWIN__) 91 | static regex_t re_regexp; 92 | 93 | char *re_comp(char *s) 94 | { 95 | if (regcomp(&re_regexp, s, 0) == 0) 96 | return NULL; 97 | else 98 | return "error"; 99 | } 100 | 101 | static int re_exec(char *s) 102 | { 103 | return regexec(&re_regexp, s, 0, 0, 0); 104 | } 105 | #endif 106 | 107 | /* 108 | read20 [-f tapefile] [-t] [-c] [-T] [-n number] pattern 109 | 110 | no tapefile ==> /dev/rmt8 111 | -t == directory listing 112 | -n == use numeric filenames in extracts, number is 1st name 113 | -T == pretend 36 bit files are 7-bit ascii 114 | -c == keep CR's in CRLF pairs. 115 | -g == keep generation numbers 116 | */ 117 | 118 | 119 | 120 | void punt (int prterrno, char *fmt, ...) 121 | { 122 | va_list ap; 123 | 124 | va_start (ap, fmt); 125 | vfprintf(stderr, fmt, ap); 126 | va_end (ap); 127 | if (prterrno) { 128 | fprintf(stderr, ": %s\n", strerror(errno)); 129 | } 130 | else 131 | fprintf(stderr, "\n"); 132 | exit(1); 133 | } 134 | 135 | 136 | /* fold -- perform case folding 137 | * 138 | * Usage: p = fold (out,in,whichway); 139 | * p = foldup (out,in); 140 | * p = folddown (out,in); 141 | * char *p,*in,*out; 142 | * enum {FOLDUP, FOLDDOWN} whichway; 143 | * 144 | * Fold performs case-folding, moving string "in" to 145 | * "out" and folding one case to another en route. 146 | * Folding may be upper-to-lower case (folddown) or 147 | * lower-to-upper case. 148 | * Foldup folds to upper case; folddown folds to lower case. 149 | * The same string may be specified as both "in" and "out". 150 | * The address of "out" is returned for convenience. 151 | * 152 | * HISTORY 153 | * 20-Nov-79 Steven Shafer (sas) at Carnegie-Mellon University 154 | * Rewritten for VAX; now uses enumerated type for fold(). The 155 | * foldup() and folddown() routines are new. 156 | * 157 | */ 158 | 159 | typedef enum { 160 | FOLDUP, FOLDDOWN} 161 | FOLDMODE; 162 | 163 | char *fold (char *out, char *in, FOLDMODE whichway) 164 | { 165 | register char *i,*o; 166 | register char lower; 167 | char upper; 168 | int delta; 169 | 170 | switch (whichway) 171 | { 172 | case FOLDUP: 173 | lower = 'a'; /* lower bound of range to change */ 174 | upper = 'z'; /* upper bound of range */ 175 | delta = 'A' - 'a'; /* amount of change */ 176 | break; 177 | case FOLDDOWN: 178 | default: 179 | lower = 'A'; 180 | upper = 'Z'; 181 | delta = 'a' - 'A'; 182 | } 183 | 184 | i = in; 185 | o = out; 186 | do { 187 | if (*i >= lower && *i <= upper) *o++ = *i++ + delta; 188 | else *o++ = *i++; 189 | } 190 | while (*i); 191 | *o = '\0'; 192 | return (out); 193 | } 194 | 195 | char *foldup (char *out, char *in) 196 | { 197 | return (fold(out,in,FOLDUP)); 198 | } 199 | 200 | char *folddown (char *out, char *in) 201 | { 202 | return (fold(out,in,FOLDDOWN)); 203 | } 204 | 205 | 206 | 207 | int masks[32] = /* bitmasks for different length fields */ 208 | { 0x00000001, 0x00000003, 0x00000007, 209 | 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 210 | 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, 211 | 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 212 | 0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff, 213 | 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff, 214 | 0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff, 215 | 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, 216 | 0xffffffff 217 | }; 218 | 219 | long getfield (char *block, /* Tape block record */ 220 | int wordoff, /* 36-bit word offset */ 221 | int bitoff, /* Bit offset of field (from msb) */ 222 | int bitlen) /* Bit length of field */ 223 | { 224 | register char *p; /* Used to point into record */ 225 | register long w32; /* First 32 bits of the 36 bit word */ 226 | int w4; /* Last 4 bits of the 36 bit word */ 227 | long w = 0; /* the word to return */ 228 | 229 | /* First, the "illegal" kludge */ 230 | if (bitoff == 0 && bitlen == 36) { 231 | bitoff = 4; 232 | bitlen = 32; 233 | } 234 | if (bitlen > 32) 235 | punt(0, "Can't get that large a field = %d!", bitlen); 236 | 237 | /* A PDP-10 (or 20) 36-bit word is laid out with the first 32 bits 238 | as the first 4 bytes and the last 4 bits are the low order 4 bits 239 | of the 5th byte. The high 4 bits of that byte should be zero */ 240 | 241 | p = block + (5*wordoff); /* Get ptr to word of interest */ 242 | w32 = *p++ & 0377; /* First byte */ 243 | w32 = (w32 << 8) | (*p++ & 0377); /* 2nd */ 244 | w32 = (w32 << 8) | (*p++ & 0377); /* 3rd */ 245 | w32 = (w32 << 8) | (*p++ & 0377); /* 4th */ 246 | w4 = *p; /* 5th */ 247 | if (w4 > 017) 248 | punt(0, "Not a PDP-10 tape! w4 = octal %o", w4); 249 | 250 | 251 | /* Get the field right justified in the word "w". 252 | There are three cases that I have to handle: 253 | [1] field is contained in w32 254 | [2] field crosses w32 and w4 255 | [3] field is contained in w4 256 | */ 257 | 258 | if (bitoff+bitlen <= 32) /* [1] field is contained in w32 */ 259 | { 260 | w = w32 >> (32 - (bitoff+bitlen)); 261 | } 262 | else if (bitoff <= 32) /* [2] field crosses boundary */ 263 | { 264 | w = (w32 << (bitoff+bitlen-32)) 265 | | (w4 >> (36 - (bitoff+bitlen))); 266 | } 267 | else /* [3] field is contained in w4 */ 268 | { 269 | w = w4 >> (36 - (bitoff+bitlen)); 270 | } 271 | w = w & masks[bitlen-1]; /* Trim to proper size */ 272 | return(w); 273 | } 274 | 275 | 276 | int lastc = 0; 277 | 278 | /* 279 | * Unpack into buffer 's' the 7 bit string stored in 'block' and 280 | * append a null char. Optionally strip CR's from CRLF pairs. 'max' 281 | * is the max number of 7-bit chars to unpack from 'block', not the 282 | * max to put into 's' (that's important!). This only works if 283 | * getstring() is always called with 'max' mod 5 == 0, except for the 284 | * last call on "contiguous" blocks. 285 | * Returns number of chars stored in output buffer. 286 | */ 287 | int getstring (char *block, /* Tape block */ 288 | char *s /* Destination string buffer */, 289 | int wordoff, /* 36-bit offset from start of tape block */ 290 | int max) /* Max number of characters to xfer into s */ 291 | { 292 | register int i; /* Counter for five characters per word */ 293 | int ct = 0; /* Number of characters loaded so far */ 294 | char *orig = s; /* Save for debugging */ 295 | int c; 296 | 297 | while (ct < max) 298 | { 299 | for (i = 0; i < 5; i++) 300 | { 301 | c = getfield(block, wordoff, i*7, 7); 302 | if (lastc == '\r' && c != '\n') 303 | *s++ = '\r'; 304 | if (c != '\r' || keepcr) 305 | *s++ = c; 306 | if (!keepcr) 307 | lastc = c; 308 | if ((ct + i + 1) == max) 309 | return (s - orig); 310 | } 311 | wordoff++; 312 | ct += 5; 313 | } 314 | printf("Fall thru in getfield\n"); 315 | fflush(stdout); 316 | *s = '\0'; 317 | return (s - orig); 318 | } 319 | 320 | 321 | /* 322 | * pendstring - return any character pending output after 323 | * last call to getstring(). Also zeros `lastc'. 324 | * Can only return '\r' or 0 for none. 325 | */ 326 | int pendstring (void) 327 | { 328 | int olastc = lastc; 329 | 330 | lastc = 0; 331 | return (olastc == '\r') ? '\r' : 0; 332 | } 333 | 334 | 335 | /* getbytes: like getstring, but ... 336 | 1) uses 8 bit bytes 337 | 2) doesn't stop on a zero 338 | */ 339 | void getbytes (char *block, /* Tape block */ 340 | char *s, /* Destination string buffer */ 341 | int wordoff, /* 36-bit offset from start of tape block */ 342 | int max) /* Max number of characters to xfer into s */ 343 | { 344 | register int i; /* Counter for five characters per word */ 345 | 346 | int ct = 0; /* Number of characters loaded so far */ 347 | /* char *orig = s; */ /* Save for debugging */ 348 | 349 | while (ct < max) 350 | { 351 | for (i = 0; i < 4; i++) 352 | { 353 | *s = getfield(block, wordoff, i*8, 8); 354 | /* if (*s == 0) return; */ 355 | s++; 356 | } 357 | wordoff++; 358 | ct += 4; 359 | } 360 | /** punt(0, "String greater than %d characters.", max); **/ 361 | } 362 | 363 | 364 | /* getwords: like getstring, but ... 365 | extracts 5 7-bit characters from a 36-bit word, and swizzles the 366 | remaining 1 bit into the last output character. 367 | */ 368 | void getwords (char *block, /* Tape block */ 369 | char *s, /* Destination string buffer */ 370 | int wordoff, /* 36-bit offset from start of tape block */ 371 | int max) /* Max number of characters to xfer into s */ 372 | { 373 | register int i; /* Counter for five characters per word */ 374 | int ct = 0; /* Number of words loaded so far */ 375 | 376 | while (ct < max) 377 | { 378 | for (i = 0; i < 5; i++) 379 | *s++ = getfield(block, wordoff, i*7, 7); 380 | s[-1] |= getfield(block, wordoff, 35, 1) << 7; 381 | wordoff++; 382 | ct++; 383 | } 384 | } 385 | 386 | 387 | /* 388 | * Unpack into buffer 's' the 8 bit string stored in 'block' and 389 | * append a null char. 390 | * Returns number of chars stored in output buffer. 391 | */ 392 | int getascii (char *block, /* Tape block */ 393 | char *s /* Destination string buffer */, 394 | int off, /* 8-bit offset from start of tape block */ 395 | int max) /* Max number of characters to xfer into s */ 396 | { 397 | memcpy (s, block + off, max); 398 | s[max] = 0; 399 | return max; 400 | } 401 | 402 | 403 | #define SecPerTick (24.*60.*60.)/0777777 404 | #define DayBaseDelta 0117213 /* Unix day 0 in Tenex format */ 405 | 406 | /* 407 | * This screws up on some of the atime's we see, including, yielding, e.g. 408 | * Fri Dec 23 23:28:16 1994 409 | * Fri Dec 23 23:28:16 1994 410 | * Tue Jan 13 07:57:03 1987 411 | */ 412 | long unixtime(char *block, int wordoff) 413 | { 414 | long int t, s; 415 | 416 | t = getfield(block, wordoff, 0, 18); /* First half is day */ 417 | t -= DayBaseDelta; /* Switch to unix base */ 418 | /* Now has # days since */ 419 | /* Jan 1, 1970 */ 420 | 421 | s = getfield(block, wordoff, 18, 18); /* 2nd half is fraction day */ 422 | s = s * SecPerTick; /* Turn into seconds */ 423 | 424 | s += t*24*60*60; /* Add day base */ 425 | return(s); 426 | } 427 | 428 | 429 | char *unixname (char *name) 430 | { 431 | static FILE *log = NULL; 432 | register char *t, *p; 433 | static char lastdir[64]; 434 | struct stat stb; 435 | int mask; 436 | register int newdir = 0; 437 | 438 | if (numflg) { /* If numeric filenames */ 439 | if (log == NULL) log = fopen(LOGFILE, "a"); 440 | fprintf(log, "%d is %s\n", number, name); 441 | sprintf(sunixname, "%d", number++); 442 | return(sunixname); 443 | } 444 | 445 | strcpy(sunixname, index(name, '<') + 1); /* trim off device */ 446 | t = rindex(sunixname, '>'); /* find end of directory */ 447 | *t = '.'; 448 | 449 | if (strncmp(lastdir, sunixname, t - sunixname)) {/* maybe new dir */ 450 | strncpy(lastdir, sunixname, t - sunixname); /* remember it */ 451 | newdir = 1; 452 | } 453 | for (p = sunixname; p <= t; p++) 454 | if (*p == '.') { 455 | if (newdir) { 456 | *p = '\0'; /* temporarily null it off */ 457 | if (stat(sunixname, &stb) < 0) { 458 | mask = umask(2); 459 | if (mkdir(sunixname, 0777) < 0) 460 | punt(1, "mkdir %s failed", sunixname); 461 | umask(mask); 462 | } 463 | } 464 | *p = '/'; 465 | } 466 | 467 | if (!genflg) { 468 | t = rindex(sunixname, '.'); /* find last . */ 469 | *t = 0; /* zap it out */ 470 | } 471 | return(sunixname); 472 | } 473 | 474 | 475 | void doDatablock (char *block) 476 | { 477 | /* max is 5 bytes per word */ 478 | static char buf[(512*5)+1]; /* A page of characters */ 479 | int ct; 480 | int maxperblock; 481 | int nout; 482 | 483 | if (debug > 10) 484 | printf("*"); 485 | if (fpFile == NULL) 486 | return; 487 | 488 | switch (bytesize) { /* only handle 7 and 8 bit bytes */ 489 | case 7: maxperblock = 512*5; break; 490 | case 8: maxperblock = 512*4; break; 491 | case 36: maxperblock = 512; break; 492 | default: return; 493 | } 494 | 495 | if (numbytes > maxperblock) 496 | ct = maxperblock; 497 | else 498 | ct = numbytes; 499 | 500 | if (binflg) { 501 | getwords(block, buf, 6, ct); 502 | fwrite(buf, 5, ct, fpFile); 503 | } 504 | else if (bytesize == 7) { 505 | nout = getstring(block, buf, 6, ct); 506 | fwrite(buf, 1, nout, fpFile); 507 | } 508 | else { /* if not 7, then 8bit */ 509 | getbytes(block, buf, 6, ct); 510 | fwrite(buf, 1, ct, fpFile); 511 | } 512 | if (ferror(fpFile)) 513 | punt(1, "Error writing %s", sunixname); 514 | numbytes -= ct; 515 | } 516 | 517 | 518 | void doSaveset (char *block, int contflag) 519 | { 520 | static char name[102]; 521 | static char ss[2]; 522 | long ssfmt, ssptr; 523 | long t; 524 | 525 | if (debug > 10) printf("\nSaveset header:"); 526 | tapeno = getfield(block, WdoffTapeNum, BtoffTapeNum, BtlenTapeNum); 527 | ssno = getfield(block, WdoffSaveSetNum, BtoffSaveSetNum, 528 | BtlenSaveSetNum); 529 | ssfmt = getfield(block, WdoffSSFmt, BtoffWord, BtlenWord); /* Get format */ 530 | ssptr = getfield(block, WdoffSSPtr, BtoffWord, BtlenWord); /* Get pointer */ 531 | // Check tape format! Otherwise breaks e.g. on Install tapes (which aren't in dumper format). 532 | if ((ssfmt < 4) || (ssfmt > 6)) { 533 | // Formats older than 4 not supported, and 6 was the highest (TOPS-20 v6-7). 534 | // If you want to support older fmts, write the code. :-) 535 | fprintf (stderr, "Bad dumper tape format %012lo\n", ssfmt); 536 | exit(1); 537 | } 538 | 539 | if (verbose) { 540 | printf("Saveset format %ld, name pointer %ld; tape %ld, saveset %ld\n", ssfmt, ssptr, tapeno, ssno); 541 | } 542 | if (ssptr == 0) { 543 | /* If there is no pointer, use default offset: for format 5-6 (T20 v6-7), SS.MSG, otherwise (T20 v4-5) BFMSG */ 544 | getstring(block, name, ssfmt > 4 ? WdoffSSMsg : WdoffSSName, sizeof(name)); 545 | } else { 546 | getstring(block, name, ssptr+WdoffSSstart, sizeof(name)); 547 | } 548 | ss[0] = pendstring(); /* superfluous */ 549 | (void) strcat(name, ss); 550 | 551 | t = unixtime(block, WdoffSSDate); 552 | if (dodir || verbose) 553 | printf("%sSaveset '%s' %s\n", contflag ? "Continued " : "", 554 | name, ctime(&t)); 555 | 556 | } 557 | 558 | 559 | /* Return 1 if topsname matches any of the "extraction" strings. */ 560 | int patternmatch (void) 561 | { 562 | register int i; 563 | 564 | for (i = 0; i < numpats; i++) 565 | if (strstr(topsname, patterns[i])) 566 | return (1); 567 | return (0); 568 | } 569 | 570 | 571 | 572 | /* Return 1 if topsname matches the regular expression. */ 573 | int expmatch (void) 574 | { 575 | register int match; 576 | 577 | if (expression) { 578 | if ((match = re_exec(topsname)) == -1) 579 | punt(0, "re_exec: internal error on %s", topsname); 580 | else 581 | return (match); 582 | } 583 | return (0); 584 | } 585 | 586 | 587 | /* Return 1 if current file number matches one selected by arg line. */ 588 | int fmatch (void) 589 | { 590 | static int widx; 591 | while (want[widx].ssnum < ssno) 592 | widx++; 593 | if (want[widx].ssnum > ssno) 594 | return 0; 595 | while (want[widx].fnum < filenum) 596 | widx++; 597 | if (want[widx].fnum > filenum) 598 | return 0; 599 | return 1; 600 | } 601 | 602 | 603 | /* 604 | * Sets a bunch of global variables to info from the fdb. 605 | * For some reason the archive tape info is garbage. 606 | */ 607 | void getfdbinfo (char *block) 608 | { 609 | 610 | timep.modtime = unixtime(block, WdoffFDB_Wrt); 611 | timep.actime = unixtime(block, WdoffFDB_Ref); 612 | timeptr = ctime(& timep.modtime) + 4; /* Skip over day-name field */ 613 | timeptr[20] = '\0'; /* Chop off \n at end */ 614 | 615 | bytesize = getfield(block, WdoffFDB_BSZ, BtoffFDB_BSZ, BtlenFDB_BSZ); 616 | numbytes = getfield(block, WdoffFDB_Size, BtoffFDB_Size,BtlenFDB_Size); 617 | pgcount = getfield(block, WdoffFDB_PGC, BtoffFDB_PGC, BtlenFDB_PGC); 618 | tprot = getfield(block, WdoffFDB_PRT, BtoffFDB_PRT, BtlenFDB_PRT); 619 | 620 | archived = getfield(block, WdoffFDB_CTL, BtoffFDB_Arc, BtlenFDB_Arc); 621 | invisible = getfield(block, WdoffFDB_CTL, BtoffFDB_Inv, BtlenFDB_Inv); 622 | offline = getfield(block, WdoffFDB_CTL, BtoffFDB_Off, BtlenFDB_Off); 623 | apgcount = getfield(block, WdoffFDB_PGC_A, BtoffFDB_PGC, BtlenFDB_PGC); 624 | /* The rest is bogus. */ 625 | tp1 = getfield(block, WdoffFDB_TP1, 0, 36); 626 | tp2 = getfield(block, WdoffFDB_TP2, 0, 36); 627 | ss1 = getfield(block, WdoffFDB_SS1, BtoffFDB_SS, BtlenFDB_SS); 628 | ss2 = getfield(block, WdoffFDB_SS2, BtoffFDB_SS, BtlenFDB_SS); 629 | tf1 = getfield(block, WdoffFDB_TF1, BtoffFDB_TF, BtlenFDB_TF); 630 | tf2 = getfield(block, WdoffFDB_TF2, BtoffFDB_TF, BtlenFDB_TF); 631 | } 632 | 633 | 634 | int t2uprot (unsigned int prot) 635 | { 636 | register unsigned tprot, uprot; 637 | register int tshift; 638 | 639 | #ifdef notdef 640 | if (f->FB_dir) { /* THIS WON'T WORK! */ 641 | punt(0, "Can't handle directory %s", topsname); 642 | prot = gtdirprot(_dirnm(jfn)); /* returns 20 fmt protection */ 643 | for (tshift=12, uprot=0; tshift >= 0; tshift -= 6) { 644 | tprot = prot >> tshift; /* pick up next field */ 645 | uprot <<= 3; 646 | if (tprot & DP_rd) 647 | uprot |= WREAD|WEXEC; /* world read, world execute */ 648 | if (tprot & (DP_cn|DP_cf)) /* allow write for either conn. */ 649 | uprot |= WWRITE; /* access or add files access */ 650 | } 651 | } 652 | else 653 | #endif 654 | { /* do it this way so easily modified-- i know it could be faster */ 655 | for (tshift=12, uprot=0; tshift >= 0; tshift -= 6) { 656 | tprot = prot >> tshift; 657 | uprot <<= 3; 658 | uprot |= (tprot >> 3) & 07; /* just r,w,x */ 659 | } 660 | } 661 | return uprot; 662 | } 663 | 664 | 665 | /* Compute the number of 8-bit host bytes needed to store a PDP-10 666 | file, using the swizzled format with five octets per 36 bits. */ 667 | static long host_octets (long file_bytes, int byte_size) 668 | { 669 | int bytes_per_word = 36 / byte_size; 670 | long words = file_bytes / bytes_per_word; 671 | int remaining_bytes = file_bytes % bytes_per_word; 672 | return 5 * words + (remaining_bytes * byte_size + 6) / 7; 673 | } 674 | 675 | void doFileHeader (char *block) 676 | { 677 | char *ts; 678 | static char prt_ar[2] = {'-', 'A'}; 679 | static char prt_inv[2] = {'-', 'I'}; 680 | static char prt_off[2] = {'-', 'O'}; 681 | 682 | if (debug > 5) 683 | printf("File Header block:\n"); 684 | 685 | filenum = getfield(block, WdoffFileNum, BtoffFileNum, BtlenFileNum); 686 | getstring(block, topsname, WdoffFLName, sizeof(topsname)); 687 | ts = index(topsname, ';'); /* Chop off ;Pprotection;Aacct */ 688 | *ts++ = pendstring(); /* superfluous */ 689 | *ts = 0; 690 | folddown(topsname, topsname); 691 | 692 | fpFile = NULL; 693 | 694 | if ( doallflag || 695 | (patterns && patternmatch()) || 696 | (expression && expmatch()) || 697 | (nselect && fmatch()) ) { 698 | getfdbinfo(block); 699 | pageno = getfield(block, WdoffPageNum, BtoffPageNum, BtlenPageNum); 700 | 701 | if (dodir || verbose) { 702 | if (verbose) 703 | printf("%3ld%6ld ", ssno, filenum); 704 | printf("%c%c%c", prt_ar[archived], prt_off[offline], 705 | prt_inv[invisible]); 706 | printf("%5d%9ld %2d %o %s %s", offline ? apgcount : pgcount, 707 | numbytes, bytesize, tprot, timeptr, topsname); 708 | if (archived && verbose >= 2) 709 | printf(" %x%4d%5d %x%4d%5d", tp1, ss1, tf1, tp2, ss2, tf2); 710 | if (pageno != 0) 711 | printf(" Split file, part 2"); 712 | } 713 | 714 | if (xflg) { 715 | if (binflg) { 716 | if (bytesize == 0) 717 | bytesize = 36; 718 | truncate_length = host_octets (numbytes, bytesize); 719 | numbytes = (numbytes + (36/bytesize) - 1) / (36 / bytesize); 720 | bytesize = 36; 721 | } 722 | /* Special hack for bad files */ 723 | else if (textflg && bytesize != 7) { 724 | if (bytesize == 0 || bytesize == 36) { 725 | bytesize = 7; 726 | numbytes *= 5; 727 | } 728 | } 729 | if ((bytesize == 7 || bytesize == 8 || binflg) && !offline) { 730 | if (pageno != 0) { /* continued file */ 731 | int missing = pageno * 512 * (bytesize == 7 ? 5 : 4); 732 | 733 | numbytes -= missing; 734 | if (!(dodir || verbose)) 735 | printf("%s: Split file, part 2", topsname); 736 | printf(": %d raw bytes missing.", missing); 737 | if (!(dodir || verbose)) 738 | putchar('\n'); 739 | } 740 | fpFile = fopen(unixname(topsname), "w"); 741 | if (fpFile == NULL) 742 | punt(1, "Can't open %s for write", sunixname); 743 | else if (verbose) 744 | printf(" Extracted."); 745 | if (fchmod(fileno(fpFile), t2uprot(tprot) & ~0111) < 0) 746 | punt(1, "fchmod on %s", sunixname); 747 | } else if (verbose) 748 | printf(" Skipping -- %s file.", 749 | offline ? "offline" : "binary"); 750 | } 751 | 752 | if (dodir || verbose) 753 | putchar('\n'); 754 | } 755 | } 756 | 757 | 758 | /*ARGSUSED*/ 759 | void doFileTrailer (char *block) 760 | { 761 | if (debug > 10) printf(" File trailer\n"); 762 | if (fpFile != NULL) { 763 | if (pendstring() == '\r') 764 | putc('\r', fpFile); 765 | if (truncate_length) { 766 | fflush (fpFile); 767 | ftruncate (fileno (fpFile), truncate_length); 768 | } 769 | if (fclose(fpFile) == EOF) 770 | punt(1, "fclose: write error on %s", sunixname); 771 | fpFile = NULL; 772 | utime(sunixname, & timep); 773 | if (numbytes != 0) 774 | printf("%s: Split file, part 1: %ld raw bytes left\n", 775 | topsname, numbytes); 776 | } 777 | } 778 | 779 | 780 | /*ARGSUSED*/ 781 | void doTapeTrailer (char *block) 782 | { 783 | if (debug > 10) printf("Tape Trailer"); 784 | } 785 | 786 | 787 | void doAnsiLabel (char *block) 788 | { 789 | char string[80]; 790 | 791 | getascii (block, string, 0, 4); 792 | if (debug > 10) printf("ANSI Label %s\n", string); 793 | 794 | if (strncmp (block, "VOL1", 4) == 0) { 795 | getascii (block, string, 4, 6); 796 | printf ("ANSI Volume Identifier: %s\n", string); 797 | } else if (strncmp (block, "HDR1", 4) == 0) { 798 | if (verbose) { 799 | getascii (block, string, 4, 17); 800 | printf ("ANSI File Indentifier: %s\n", string); 801 | getascii (block, string, 21, 6); 802 | printf ("File Set: %s\n", string); 803 | getascii (block, string, 27, 4); 804 | printf ("File Section: %s\n", string); 805 | } 806 | } else if (strncmp (block, "HDR2", 4) == 0) { 807 | if (verbose) { 808 | getascii (block, string, 4, 1); 809 | printf ("Record Format: %s\n", string); 810 | getascii (block, string, 5, 5); 811 | printf ("Block Length: %s\n", string); 812 | getascii (block, string, 10, 5); 813 | printf ("Record Length: %s\n", string); 814 | } 815 | } 816 | } 817 | 818 | 819 | int compwant(const void *wa1, const void *wa2) 820 | { 821 | const struct want *w1 = wa1; 822 | const struct want *w2 = wa2; 823 | int sdif; 824 | 825 | if ((sdif = w1->ssnum - w2->ssnum)) 826 | return sdif; 827 | return (w1->fnum - w2->fnum); 828 | } 829 | 830 | 831 | int main (int argc, char *argv[]) 832 | { 833 | char *tape; /* Pathname for tape device/file */ 834 | char *tapeblock; 835 | int rc; 836 | int rtype; 837 | 838 | if (! (tape = getenv ("TAPE"))) 839 | tape = TAPE; 840 | 841 | /* Do switch parsing */ 842 | 843 | while(argc>1 && argv[1][0] == '-'){ 844 | switch(argv[1][1]){ 845 | case 'f': 846 | if (argc <= 2) 847 | punt(0, "Need filename after -f"); 848 | tape = argv[2]; 849 | argc--; argv++; 850 | break; 851 | case 'T': /* Force text mode on "binary" files */ 852 | textflg = 1; 853 | break; 854 | case 'b': /* Force extracting all files */ 855 | binflg = 1; 856 | break; 857 | case 't': /* directory listing */ 858 | dodir = 1; 859 | break; 860 | case 'x': /* extract */ 861 | xflg = 1; 862 | break; 863 | case 'v': /* verbosity */ 864 | verbose++; 865 | break; 866 | case 'g': /* keep gen number */ 867 | genflg++; 868 | break; 869 | case 'd': 870 | debug = atoi(&argv[1][2]); 871 | fprintf(stderr, "Debug value set to %d\n", debug); 872 | break; 873 | case 'n': /* numeric output filenames */ 874 | if (argc <= 2) 875 | punt(0, "Need number after -n"); 876 | number = atoi(argv[2]); /* First file name */ 877 | numflg = 1; 878 | argc--; argv++; 879 | break; 880 | case 'c': /* keep CR`s in CR/LF pairs */ 881 | keepcr++; 882 | break; 883 | case 'e': /* regular expression */ 884 | if (argc <= 2) 885 | punt(0, "Need expression after -e"); 886 | if (expression) 887 | punt(0, "Only one regexp allowed"); 888 | expression = argv[2]; 889 | if ((re_comp_error = re_comp(expression)) != 0) 890 | punt(0, "re_comp: %s", re_comp_error); 891 | argc--; argv++; 892 | break; 893 | case 'S': /* selected save set number */ 894 | if (argc <= 2) 895 | punt(0, "Need save set number after -S"); 896 | cursswant = atoi(argv[2]); 897 | argc--; argv++; 898 | break; 899 | case 'F': /* selected file numbers */ 900 | if (argc <= 2) 901 | punt(0, "Need file number(s) after -F"); 902 | for (argc -= 2, argv += 2; 903 | argc && isdigit(**argv); 904 | argc--, argv++, nselect++) { 905 | want[nselect].ssnum = cursswant; 906 | want[nselect].fnum = atoi(*argv); 907 | } 908 | argc += 2; argv -= 2; 909 | break; 910 | default: 911 | punt(0, "unknown flag %s", argv[1]); 912 | } 913 | argc--; argv++; 914 | } 915 | 916 | if (!xflg && !dodir) 917 | punt(0, "Need either '-x' or '-t' option."); 918 | 919 | if (textflg && binflg) 920 | punt(0, "'-T' and '-b' are mutually exclusive."); 921 | 922 | if (argc > 1) { 923 | patterns = &argv[1]; 924 | numpats = argc - 1; 925 | } 926 | doallflag = !(patterns || expression || nselect); 927 | if (nselect) 928 | qsort((char *)want, nselect, sizeof (struct want), compwant); 929 | 930 | tape_handle = opentape (tape, 0, 0); 931 | if (! tape_handle) 932 | punt(1, "Can't open tape '%s'", tape); 933 | 934 | label = 0; 935 | rc = 0; 936 | for ( ; ; ) /* Loop till end of tape */ 937 | { 938 | /*** Read a block ***/ 939 | if (rc == 0) { 940 | rc = getrec (tape_handle, tapeblocka, TAPEBLK); 941 | if (debug > 99) 942 | printf("rc=%d\n", rc); 943 | if ((rc % (518*5)) != 0) { 944 | if (rc == 80) { 945 | doAnsiLabel(tapeblocka); 946 | label = 1; 947 | rc = 0; 948 | continue; 949 | } 950 | if (rc != 0) 951 | punt(1, "Oops. Read block len = %d", rc); 952 | } 953 | if (rc == 0) { 954 | if (label) { 955 | label = 0; 956 | continue; 957 | } 958 | if (verbose) 959 | printf("\nEnd of tape.\n"); 960 | exit(0); /* Normal exit */ 961 | } 962 | tapeblock = tapeblocka; 963 | rc = rc - 518*5; 964 | } 965 | else { 966 | tapeblock = tapeblock + 518*5; 967 | rc = rc - 518*5; 968 | } 969 | 970 | /*** Do something with it ***/ 971 | switch(rtype = -getfield(tapeblock, 972 | WdoffRectype, BtoffRectype, BtlenRectype)) 973 | { 974 | case RectypeData: /* Data block */ 975 | doDatablock(tapeblock); 976 | break; 977 | 978 | case RectypeTphd: /* Saveset header */ 979 | doSaveset(tapeblock, 0); 980 | break; 981 | 982 | case RectypeFlhd: /* File header */ 983 | doFileHeader(tapeblock); 984 | break; 985 | 986 | case RectypeFltr: /* File trailer */ 987 | doFileTrailer(tapeblock); 988 | break; 989 | 990 | case RectypeTptr: /* Tape trailer */ 991 | doTapeTrailer(tapeblock); 992 | break; 993 | 994 | case RectypeUsr: /* User directory info ? */ 995 | if (verbose >= 3) 996 | fprintf(stderr, "Directory record skipped\n"); 997 | break; 998 | 999 | case RectypeCtph: /* Continued saveset hdr */ 1000 | doSaveset(tapeblock, 1); 1001 | break; 1002 | 1003 | case RectypeFill: /* Fill record */ 1004 | if (verbose >= 3) 1005 | fprintf(stderr, "Fill record skipped\n"); 1006 | break; 1007 | 1008 | default: 1009 | punt(0, "Unknown record type 0x%x", rtype); 1010 | break; 1011 | } 1012 | } 1013 | } 1014 | 1015 | 1016 | 1017 | #ifdef notdef 1018 | #define HOUR 3600 1019 | #define DAY (HOUR*24) 1020 | #define DAY0 40587 /* number of days between tops20 0 day and Unix 0 day */ 1021 | 1022 | #define makeword(l, r) ( ((l) << 18) | (r) ) 1023 | #define getright(b) ( (b) & 0777777 ) 1024 | #define getleft(b) ( (b) >> 18 ) 1025 | 1026 | /* Convert Tops-20 to Unix time -- curently incomplete due to 32 < 36 bits */ 1027 | int 1028 | _t2utim(t) 1029 | unsigned t; 1030 | { 1031 | register ticks, rh, secs; 1032 | 1033 | ticks = t - makeword(DAY0, 0); 1034 | rh = getright(ticks) * DAY; 1035 | secs = rh >> 18; 1036 | if (rh % makeword(1,0) > 0400000) 1037 | secs++; /* round up */ 1038 | return (getleft(ticks) * DAY) + secs; 1039 | } 1040 | #endif 1041 | 1042 | -------------------------------------------------------------------------------- /t10backup.c: -------------------------------------------------------------------------------- 1 | /* 2 | Dump contents of Tops-10 BACKUP tapes, or tape images. 3 | 4 | Original author unknown. 5 | Modified by Eric Smith to use John Wilson's tapeio library. 6 | 7 | Copyright 1999 Eric Smith 8 | 9 | This program is free software; you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License version 2 as published 11 | by the Free Software Foundation. Note that permission is not granted 12 | to redistribute this program under the terms of any other version of the 13 | General Public License. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License 21 | along with this program. If not, see . 22 | */ 23 | 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "t10backup.h" 35 | #include "tapeio.h" 36 | 37 | #define bool long 38 | #define false 0 39 | #define true 1 40 | 41 | #define MAX_FILE_SPEC 1024 42 | 43 | #define RAWSIZE (5*(32+512)) 44 | 45 | #define endof(s) (strchr(s, (char) 0)) 46 | 47 | char *progname; 48 | 49 | 50 | tape_handle_t tape; /* Source "tape". */ 51 | 52 | 53 | bool eightbit = false; /* Status of -8 (eight-bit) flag. */ 54 | bool buildtree = false; /* Status of -d (build trees) flag. */ 55 | bool interchange = false; /* Status of -i (interchange) flag. */ 56 | long verbose = 0; /* Status of -v (verbose) flag. */ 57 | 58 | 59 | char* argfiles [MAX_FILE_SPEC]; /* File spec's to extract. */ 60 | long argcount; /* Number of them. */ 61 | 62 | 63 | unsigned char rawdata[RAWSIZE]; /* Raw data for a tape block. */ 64 | 65 | long headlh[32], headrh[32]; /* Header block from tape. */ 66 | long datalh[512], datarh[512]; /* Data block from tape. */ 67 | 68 | long prevSEQ; /* SEQ number of previous block. */ 69 | long currentfilenumber; 70 | 71 | long defercount; /* Count of defered output bytes. */ 72 | 73 | bool extracting; 74 | FILE* destination; 75 | 76 | /* Tape information: */ 77 | 78 | char systemname[100]; 79 | char savesetname[100]; 80 | 81 | /* File information: */ 82 | 83 | long a_bsiz; /* For now. */ 84 | long a_alls; 85 | long a_mode; 86 | long a_leng; 87 | 88 | char filedev[100]; /* Device: */ 89 | char filedir[100]; /* [ufd] */ 90 | char filename[100]; /* file name. */ 91 | char fileext[100]; /* extension. */ 92 | 93 | char filespec[7][100]; /* [0]: device:ufd. */ 94 | /* [1-5]: sfd's, stored directly here. */ 95 | /* [6]: file.ext */ 96 | 97 | char cname[100]; /* Canonical name. */ 98 | 99 | 100 | void print_usage (FILE *f) 101 | { 102 | fprintf (f, "Usage: %s [options] [filespecs...]\n", progname); 103 | fprintf (f, "Options:\n" 104 | " -t list directory\n" 105 | " -x extract files\n" 106 | " -f read input from file\n" 107 | " /dev/* tape drive\n" 108 | " host:file rmt server\n" 109 | " file tape image file\n" 110 | " - tape image from stdin\n" 111 | " -s use save sets in range\n" 112 | " e.g. '-s 2' for only save set 2\n" 113 | " '-s 2,' for save set 2 through EOT\n" 114 | " '-s 3,5' for save sets 3 through 5\n" 115 | " -v verbose\n" 116 | " -vv very verbose\n" 117 | " -8 eight bit mode\n" 118 | " -i interchange mode\n" 119 | " -d build directory tree\n"); 120 | } 121 | 122 | 123 | void fatal (int retval, char *fmt, ...) 124 | { 125 | va_list ap; 126 | 127 | if (fmt) 128 | { 129 | fprintf (stderr, "%s: ", progname); 130 | va_start (ap, fmt); 131 | vfprintf (stderr, fmt, ap); 132 | va_end (ap); 133 | } 134 | 135 | if (retval == 1) 136 | print_usage (stderr); 137 | 138 | exit (retval); 139 | } 140 | 141 | 142 | void warning (char *fmt, ...) 143 | { 144 | va_list ap; 145 | 146 | fprintf (stderr, "%s: ", progname); 147 | va_start (ap, fmt); 148 | vfprintf (stderr, fmt, ap); 149 | va_end (ap); 150 | } 151 | 152 | 153 | /* unpackheader unpacks the header block from the raw stream. */ 154 | 155 | void unpackheader (void) 156 | { 157 | unsigned char* rawptr; 158 | long i, left, right; 159 | unsigned char c; 160 | 161 | rawptr = & rawdata [0]; 162 | 163 | for (i = 0; i < 32; i++) 164 | { 165 | left = * (rawptr++) << 10; 166 | left |= * (rawptr++) << 2; 167 | left |= (c = * (rawptr++)) >> 6; 168 | right = (c & 077) << 12; 169 | right |= * (rawptr++) << 4; 170 | right |= * (rawptr++) & 017; 171 | headlh [i] = left; 172 | headrh [i] = right; 173 | if (verbose > 1) 174 | { 175 | printf("\n%li l=%ld, r=%ld", i, left, right); 176 | } 177 | } 178 | } 179 | 180 | 181 | /* unpackdata unpacks the data block from the raw stream. */ 182 | 183 | void unpackdata (void) 184 | { 185 | unsigned char* rawptr; 186 | long i, left, right; 187 | unsigned char c; 188 | 189 | rawptr = & rawdata [32*5]; 190 | 191 | for (i = 0; i < 512; i++) 192 | { 193 | left = * (rawptr++) << 10; 194 | left |= * (rawptr++) << 2; 195 | left |= (c = * (rawptr++)) >> 6; 196 | right = (c & 077) << 12; 197 | right |= * (rawptr++) << 4; 198 | right |= * (rawptr++) & 017; 199 | datalh [i] = left; 200 | datarh [i] = right; 201 | } 202 | } 203 | 204 | 205 | /* pars_5chars reads five ASCII chars from a machine word. */ 206 | 207 | void pars_5chars (long index, char *store) 208 | { 209 | long l, r; 210 | 211 | l = datalh [index]; 212 | r = datarh [index]; 213 | 214 | store [0] = (0177 & (l >> 11)); 215 | store [1] = (0177 & (l >> 4)); 216 | store [2] = (0177 & ((l << 3) | ((r >> 15) & 017))); 217 | store [3] = (0177 & (r >> 8)); 218 | store [4] = (0177 & (r >> 1)); 219 | store [4] |= (r&1) << 7; 220 | } 221 | 222 | 223 | /* pars_asciz stores asciz text from data */ 224 | 225 | void pars_asciz (long index, char *store) 226 | { 227 | long words; 228 | 229 | words = datarh [index++]; 230 | while ((words--) > 0) 231 | { 232 | pars_5chars (index++, store); 233 | store += 5; 234 | } 235 | * store = (char) 0; 236 | } 237 | 238 | 239 | /* pars_o_name parses an o$name block from data. */ 240 | 241 | void pars_o_name (long index) 242 | { 243 | long lastw; 244 | 245 | lastw = index + datarh [index]; 246 | ++index; 247 | while (index < lastw) 248 | { 249 | switch (datalh [index]) 250 | { 251 | case 0: index = lastw; break; 252 | case 1: pars_asciz (index, filedev); break; 253 | case 2: pars_asciz (index, filename); break; 254 | case 3: pars_asciz (index, fileext); break; 255 | case 32: pars_asciz (index, filedir); break; 256 | case 33: pars_asciz (index, filespec [1]); break; 257 | case 34: pars_asciz (index, filespec [2]); break; 258 | case 35: pars_asciz (index, filespec [3]); break; 259 | case 36: pars_asciz (index, filespec [4]); break; 260 | case 37: pars_asciz (index, filespec [5]); break; 261 | } 262 | index += datarh [index]; 263 | } 264 | } 265 | 266 | 267 | void pars_o_attr (long index) 268 | { 269 | /* parse off file attribute block */ 270 | ++index; 271 | a_bsiz = datarh [index + A_BSIZ]; /* for now... */ 272 | a_alls = datarh [index + A_ALLS]; /* for now... */ 273 | a_mode = datarh [index + A_MODE]; /* for now... */ 274 | a_leng = datarh [index + A_LENG]; /* for now... */ 275 | } 276 | 277 | 278 | void pars_o_dirt (long index) 279 | { 280 | /* parse off directory attribute block */ 281 | } 282 | 283 | 284 | void pars_o_sysn (long index) 285 | { 286 | pars_asciz (index, systemname); 287 | } 288 | 289 | 290 | void pars_o_ssnm (long index) 291 | { 292 | pars_asciz (index, savesetname); 293 | } 294 | 295 | 296 | void zerotapeinfo (void) 297 | { 298 | systemname [0] = (char) 0; 299 | savesetname [0] = (char) 0; 300 | } 301 | 302 | 303 | void zerofileinfo (void) 304 | { 305 | filedev [0] = (char) 0; 306 | filedir [0] = (char) 0; 307 | filename [0] = (char) 0; 308 | fileext [0] = (char) 0; 309 | 310 | filespec [0][0] = (char) 0; 311 | filespec [1][0] = (char) 0; 312 | filespec [2][0] = (char) 0; 313 | filespec [3][0] = (char) 0; 314 | filespec [4][0] = (char) 0; 315 | filespec [5][0] = (char) 0; 316 | filespec [6][0] = (char) 0; 317 | 318 | cname [0] = (char) 0; 319 | } 320 | 321 | 322 | /* unpackinfo picks non-data information from data block. */ 323 | 324 | void unpackinfo (void) 325 | { 326 | long index; 327 | 328 | unpackdata (); 329 | 330 | index = 0; 331 | while (index < headrh [G_LND]) 332 | { 333 | switch (datalh [index]) 334 | { 335 | case 1: pars_o_name (index); break; 336 | case 2: pars_o_attr (index); break; 337 | case 3: pars_o_dirt (index); break; 338 | case 4: pars_o_sysn (index); break; 339 | case 5: pars_o_ssnm (index); break; 340 | } 341 | index += datarh [index]; 342 | } 343 | } 344 | 345 | 346 | void printtapeinfo (void) 347 | { 348 | if (verbose) 349 | { 350 | if (* savesetname != (char) 0) 351 | printf ("Saveset name: %s\n", savesetname); 352 | if (* systemname != (char) 0) 353 | printf ("Written on: %s\n", systemname); 354 | } 355 | } 356 | 357 | 358 | void downcase (char *s) 359 | { 360 | while (*s != (char) 0) { 361 | if (isupper(*s)) *s = tolower(*s); 362 | s++; 363 | } 364 | } 365 | 366 | 367 | void buildfilenames (void) 368 | { 369 | long i; 370 | 371 | if (* filedev != (char) 0) 372 | sprintf (filespec [0], "%s:%s", filedev, filedir); 373 | else 374 | sprintf (filespec [0], "%s", filedir); 375 | 376 | sprintf (filespec [6], "%s.%s", filename, fileext); 377 | 378 | for (i = 0; i < 7; i++) 379 | downcase (filespec [i]); 380 | 381 | sprintf (cname, "%s", filespec [0]); 382 | for (i = 1; i < 6; i++) 383 | { 384 | if (* filespec [i] != (char) 0) 385 | sprintf (endof (cname), ".%s", filespec [i]); 386 | } 387 | if (* cname != (char) 0) 388 | sprintf (endof (cname), "..%s", filespec [6]); 389 | else 390 | sprintf (cname, "%s", filespec [6]); 391 | } 392 | 393 | 394 | void printfileinfo (void) 395 | { 396 | buildfilenames (); 397 | printf ("%3ld %s", currentfilenumber, cname); 398 | if (verbose) 399 | { 400 | printf (" (%ld) alloc:%ld, mode:%lo, len:%ld", a_bsiz, a_alls, a_mode, a_leng); 401 | } 402 | printf ("\n"); 403 | } 404 | 405 | 406 | /* readblock reads one logical block from the input stream. */ 407 | /* The header is unpacked into head{l,r}; the data is not. */ 408 | 409 | int readblock (void) 410 | { 411 | long i; 412 | i = getrec (tape, rawdata, RAWSIZE); 413 | if (i == 0) 414 | return (0); 415 | if (i != RAWSIZE) 416 | { 417 | fprintf (stderr, "record length %ld, expected %d\n", i, RAWSIZE); 418 | while (i++ < RAWSIZE) rawdata [i] = (char) 0; 419 | } 420 | unpackheader (); 421 | return (1); 422 | } 423 | 424 | 425 | /* Disk file output routines: */ 426 | 427 | void WriteBlock (void) 428 | { 429 | char buffer [5*512]; 430 | long bufpos, index; 431 | 432 | unpackdata (); 433 | 434 | for (index = headrh [G_LND], bufpos = 0; 435 | index < (headrh [G_LND] + headrh [G_SIZE]); index++) 436 | { 437 | pars_5chars (index, &buffer [bufpos]); 438 | bufpos += 5; 439 | } 440 | 441 | if (headlh [G_FLAGS] & GF_EOF) 442 | { 443 | for (index = 1; index < (eightbit ? 4 : 5); index++) 444 | { 445 | if (buffer[bufpos - 1] == (char) 0) 446 | bufpos--; 447 | } 448 | } 449 | 450 | (void) fwrite (buffer, sizeof (char), bufpos, destination); 451 | } 452 | 453 | 454 | /* OpenOutput opens the output file, according to -d and -i flags. */ 455 | 456 | bool OpenOutput (void) 457 | { 458 | struct stat statbuf; 459 | char oname [100]; 460 | long i; 461 | 462 | defercount = 0; 463 | 464 | if (interchange) 465 | destination = fopen (filespec [6], "w"); 466 | else if (! buildtree) 467 | destination = fopen(cname, "w"); 468 | else 469 | { 470 | for (i = 0, oname [0] = (char) 0; i < 6; i++) 471 | { 472 | if (* filespec [i] == (char) 0) 473 | break; 474 | sprintf (endof (oname), "%s", filespec [i]); 475 | if (stat (oname, & statbuf) != 0) 476 | { 477 | if (mkdir (oname, 0777) != 0) 478 | { 479 | warning ("cannot create %s/\n", oname); 480 | return (false); 481 | } 482 | } 483 | sprintf (endof (oname), "/"); 484 | } 485 | sprintf (endof (oname), "%s", filespec [6]); 486 | destination = fopen (oname, "w"); 487 | } 488 | 489 | return (destination != NULL); 490 | } 491 | 492 | void CloseOutput (void) 493 | { 494 | /* Close output file after us. */ 495 | } 496 | 497 | /* Argmatch checks if the current file matches the given argument: */ 498 | 499 | bool argmatch (char *arg) 500 | { 501 | long target; 502 | char* f; 503 | char* p; 504 | char* s; 505 | 506 | if (*arg == '#') 507 | { 508 | (void) sscanf (arg, "#%ld", & target); 509 | return (target == currentfilenumber); 510 | } 511 | 512 | if (*arg == '*') 513 | return (1); 514 | 515 | for (f = cname; *f != (char) 0; f++) 516 | { 517 | for (p = f, s = arg; (*s != (char) 0) && (*p == *s); p++, s++) 518 | ; 519 | if (*s == (char) 0) 520 | return (true); 521 | } 522 | return (false); 523 | } 524 | 525 | /* doextract performs the job of "backup -x ..." */ 526 | 527 | int doextract (void) 528 | { 529 | int got_blocks = 0; 530 | long i; 531 | 532 | currentfilenumber = 0; 533 | extracting = false; 534 | 535 | for (;;) 536 | { 537 | if (! readblock ()) 538 | return (got_blocks); 539 | 540 | got_blocks = 1; 541 | 542 | if (headrh [G_SEQ] == prevSEQ) 543 | continue; 544 | 545 | if (headrh [G_TYPE] == T_FILE) 546 | { 547 | if (headlh [G_FLAGS] & GF_SOF) 548 | { 549 | currentfilenumber++; 550 | zerofileinfo (); 551 | unpackinfo (); 552 | buildfilenames (); 553 | for (i = 0; i < argcount; i++) 554 | { 555 | if (argmatch (argfiles [i])) 556 | { 557 | if (*argfiles[i] == '#') 558 | { 559 | /* Maybe do a pure shift here? */ 560 | argfiles [i] = argfiles [--argcount]; 561 | } 562 | extracting = true; 563 | break; 564 | } 565 | } 566 | if (extracting) 567 | { 568 | if (OpenOutput ()) 569 | { 570 | if (verbose) 571 | { 572 | printf ("Extracting %s", cname); 573 | fflush (stdout); 574 | } 575 | } 576 | else 577 | { 578 | warning ("can't open %s for output\n", cname); 579 | extracting = false; 580 | } 581 | } 582 | } 583 | if (extracting) 584 | { 585 | WriteBlock (); 586 | if (headlh [G_FLAGS] & GF_EOF) 587 | { 588 | (void) fclose (destination); 589 | extracting = false; 590 | if (verbose) 591 | printf ("\n"); 592 | if (argcount == 0) 593 | { 594 | skipfile (tape, 1); 595 | return (1); 596 | } 597 | } 598 | } 599 | } 600 | prevSEQ = headrh[G_SEQ]; 601 | } 602 | } 603 | 604 | /* dodirectory performs the job of "backup -t ..." */ 605 | 606 | int dodirectory (void) 607 | { 608 | int got_blocks = 0; 609 | currentfilenumber = 0; 610 | 611 | for (;;) 612 | { 613 | if (! readblock ()) 614 | return (got_blocks); 615 | 616 | got_blocks = 1; 617 | 618 | if (headrh [G_SEQ] == prevSEQ) 619 | continue; 620 | 621 | if (headrh [G_TYPE] == T_BEGIN) 622 | { 623 | zerotapeinfo (); 624 | unpackinfo (); 625 | printtapeinfo (); 626 | } 627 | if (headrh [G_TYPE] == T_FILE) 628 | { 629 | if (headlh [G_FLAGS] & GF_SOF) 630 | { 631 | ++currentfilenumber; 632 | zerofileinfo(); 633 | unpackinfo(); 634 | printfileinfo(); 635 | } 636 | } 637 | prevSEQ = headrh [G_SEQ]; 638 | } 639 | } 640 | 641 | /* command decoder and dispatcher */ 642 | 643 | void checkarg (char *arg) 644 | { 645 | long i; 646 | char c; 647 | 648 | if (*arg == '#') 649 | { 650 | if (sscanf (arg, "#%ld%c", & i, & c) != 1) 651 | fatal (1, "bad argument: '%s'\n", arg); 652 | } 653 | } 654 | 655 | 656 | void parse_range (char *s, int *first, int *last) 657 | { 658 | char *s2 = s; 659 | 660 | (* first) = (* last) = strtol (s, & s, 0); 661 | if (! s) 662 | return; 663 | if (*(s++) != ',') 664 | goto badrange; 665 | if (! *s) 666 | { 667 | (* last) = INT_MAX; 668 | return; 669 | } 670 | (* last) = strtol (s, & s, 0); 671 | if (! s) 672 | goto badrange; 673 | if ((* first) > (* last)) 674 | goto badrange; 675 | return; 676 | 677 | badrange: 678 | fatal (1, "bad range syntax: '%s'\n", s2); 679 | } 680 | 681 | 682 | int main (int argc, char *argv[]) 683 | { 684 | long i; 685 | char action = '\0'; 686 | char* inputname = NULL; 687 | char *arg; 688 | int first_save_set = 0; 689 | int last_save_set = 0; 690 | int current_save_set = 0; 691 | 692 | progname = argv [0]; 693 | 694 | argcount = 0; 695 | 696 | while (--argc > 0) 697 | { 698 | ++argv; 699 | arg = argv [0]; 700 | if (arg [0] == '-') 701 | { 702 | while (*++arg) 703 | switch (*arg) 704 | { 705 | case '8': 706 | eightbit = true; break; 707 | case 'd': 708 | buildtree = true; break; 709 | case 'f': 710 | if (--argc < 0) 711 | fatal (1, "input file name missing\n"); 712 | inputname = (++argv) [0]; 713 | break; 714 | case 'i': 715 | interchange = true; break; 716 | case 's': 717 | if ((--argc < 0) || ((++argv)[0][0] == '-')) 718 | fatal (1, "file skip count missing\n"); 719 | parse_range (argv [0], & first_save_set, & last_save_set); 720 | /* 721 | fprintf (stderr, "save set range %d..%d\n", 722 | first_save_set, last_save_set); 723 | */ 724 | break; 725 | case 't': 726 | case 'x': 727 | action = *arg; break; 728 | case 'v': 729 | verbose++; break; 730 | default: 731 | fatal (1, "bad option %c\n", arg [1]); 732 | } 733 | } 734 | else 735 | { 736 | if (argcount >= MAX_FILE_SPEC) 737 | fatal (1, "too many file specs\n"); 738 | argfiles [argcount++] = argv [0]; 739 | } 740 | } 741 | 742 | if (! action) 743 | fatal (1, "either -t or -x must be specified\n"); 744 | 745 | for (i = 0; i < argcount; i++) 746 | checkarg (argfiles[i]); 747 | 748 | if (inputname == NULL) 749 | fatal (1, "no input file given\n"); 750 | 751 | tape = opentape (inputname, 0, 0); 752 | 753 | if (! tape) 754 | fatal (1, "can't open %s for input\n", inputname); 755 | 756 | if (first_save_set > 0) 757 | { 758 | skipfile (tape, first_save_set); 759 | current_save_set = first_save_set; 760 | } 761 | 762 | while (current_save_set <= last_save_set) 763 | { 764 | int ok = 0; 765 | switch (action) 766 | { 767 | case 't': ok = dodirectory (); break; 768 | case 'x': ok = doextract (); break; 769 | } 770 | if (! ok) 771 | break; 772 | current_save_set++; 773 | } 774 | 775 | return (0); 776 | } 777 | 778 | -------------------------------------------------------------------------------- /t10backup.h: -------------------------------------------------------------------------------- 1 | 2 | /* Record types: */ 3 | 4 | #define T_LABEL 1 /* Label. */ 5 | #define T_BEGIN 2 /* Start of SaveSet. */ 6 | #define T_END 3 /* End of SaveSet. */ 7 | #define T_FILE 4 /* File data. */ 8 | #define T_UFD 5 /* UFD data. */ 9 | #define T_EOV 6 /* End of volume. */ 10 | #define T_COMM 7 /* Comment. */ 11 | #define T_CONT 8 /* Continuation. */ 12 | 13 | /* Offsets into header block: */ 14 | 15 | #define G_TYPE 0 /* Record type. */ 16 | #define G_SEQ 1 /* Sequence #. */ 17 | #define G_RTNM 2 /* Relative tape #. */ 18 | #define G_FLAGS 3 /* Flags: */ 19 | #define GF_EOF 0400000 /* End of file. */ 20 | #define GF_RPT 0200000 /* Repeat of last record. */ 21 | #define GF_NCH 0100000 /* Ignore checksum. */ 22 | #define GF_SOF 0040000 /* Start of file. */ 23 | #define G_CHECK 4 /* Checksum. */ 24 | #define G_SIZE 5 /* Size of data in this block. */ 25 | #define G_LND 6 /* Length of non-data block. */ 26 | 27 | /* Non-data block types: */ 28 | 29 | #define O_NAME 1 /* File name. */ 30 | #define O_ATTR 2 /* File attributes. */ 31 | #define O_DIRECT 3 /* Directory attributes. */ 32 | #define O_SYSNAME 4 /* System name block. */ 33 | #define O_SAVESET 5 /* SaveSet name block. */ 34 | 35 | /* Offsets in attribute block: */ 36 | 37 | #define A_FHLN 0 /* header length word */ 38 | #define A_FLGS 1 /* flags */ 39 | #define A_WRIT 2 /* creation date/time */ 40 | #define A_ALLS 3 /* allocated size */ 41 | #define A_MODE 4 /* mode */ 42 | #define A_LENG 5 /* length */ 43 | #define A_BSIZ 6 /* byte size */ 44 | #define A_VERS 7 /* version */ 45 | #define A_PROT 8 /* protection */ 46 | #define A_ACCT 9 /* byte pointer account string */ 47 | #define A_NOTE 10 /* byte pointer to anonotation string */ 48 | #define A_CRET 11 /* creation date/time of this generation */ 49 | #define A_REDT 12 /* last read date/time of this generation */ 50 | #define A_MODT 13 /* monitor set last write date/time */ 51 | #define A_ESTS 14 /* estimated size in words */ 52 | #define A_RADR 15 /* requested disk address */ 53 | #define A_FSIZ 16 /* maximum file size in words */ 54 | #define A_MUSR 17 /* byte ptr to id of last modifier */ 55 | #define A_CUSR 18 /* byte ptr to id of creator */ 56 | #define A_BKID 19 /* byte ptr to save set of previous backup */ 57 | #define A_BKDT 20 /* date/time of last backup */ 58 | #define A_NGRT 21 /* number of generations to retain */ 59 | #define A_NRDS 22 /* nbr opens for read this generation */ 60 | #define A_NWRT 23 /* nbr opens for write this generation */ 61 | #define A_USRW 24 /* user word */ 62 | #define A_PCAW 25 /* privileged customer word */ 63 | #define A_FTYP 26 /* file type and flags */ 64 | #define A_FBSZ 27 /* byte sizes */ 65 | #define A_FRSZ 28 /* record and block sizes */ 66 | #define A_FFFB 29 /* application/customer word */ 67 | 68 | /* T_BEGIN, T_END & T_CONT header offsets: */ 69 | 70 | #define S_DATE 12 71 | #define S_FORMAT 13 72 | #define S_BVER 14 73 | #define S_MONTYP 15 74 | #define S_SVER 16 75 | #define S_APR 17 76 | #define S_DEVICE 18 77 | #define S_MTCHAR 19 78 | #define S_REELID 20 79 | #define S_LTYPE 21 80 | 81 | -------------------------------------------------------------------------------- /tapecopy.c: -------------------------------------------------------------------------------- 1 | /* 2 | tapecopy 3 | 4 | This program uses a modified version of John Wilson's tapeio library to 5 | copy tapes between any of the following: 6 | 7 | local tape drives - pathname starting with /dev/ 8 | remote tape drives (rmt) - pathname containing a colon 9 | Wilson-format tape image files - any other pathname 10 | 11 | Copyright 1999, 2000 Eric Smith 12 | 13 | This program is free software; you can redistribute it and/or modify 14 | it under the terms of the GNU General Public License version 2 as published 15 | by the Free Software Foundation. Note that permission is not granted 16 | to redistribute this program under the terms of any other version of the 17 | General Public License. 18 | 19 | This program is distributed in the hope that it will be useful, 20 | but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | GNU General Public License for more details. 23 | 24 | You should have received a copy of the GNU General Public License 25 | along with this program. If not, see . 26 | */ 27 | 28 | #include "stdio.h" 29 | #include "stdlib.h" 30 | #include "stdarg.h" 31 | 32 | #include "tapeio.h" 33 | 34 | #define MAX_REC_LEN 32768 35 | 36 | char *progname; 37 | 38 | char *buf; 39 | 40 | void print_usage (FILE *f) 41 | { 42 | fprintf (f, "Usage: %s [-v] in out\n", progname); 43 | } 44 | 45 | void fatal (int retval, char *fmt, ...) 46 | { 47 | va_list ap; 48 | 49 | if (fmt) 50 | { 51 | fprintf (stderr, "%s: ", progname); 52 | va_start (ap, fmt); 53 | vfprintf (stderr, fmt, ap); 54 | va_end (ap); 55 | } 56 | 57 | if (retval == 1) 58 | print_usage (stderr); 59 | 60 | exit (retval); 61 | } 62 | 63 | int main (int argc, char *argv[]) 64 | { 65 | int file = 0; 66 | unsigned long filebytes = 0; 67 | unsigned long tapebytes = 0; 68 | int prevlen = -2; 69 | int lencount = 0; 70 | int firstrec = 0; 71 | int len; 72 | int verbose = 0; 73 | char *srcfn = NULL; 74 | char *destfn = NULL; 75 | tape_handle_t src = NULL; 76 | tape_handle_t dest = NULL; 77 | 78 | progname = argv [0]; 79 | 80 | while (++argv, --argc) 81 | { 82 | if ((argv [0][0] == '-') && (argv [0][1] != '\0')) 83 | { 84 | if (argv [0][1] == 'v') 85 | verbose++; 86 | else 87 | fatal (1, "unrecognized option '%s'\n", argv [0]); 88 | } 89 | else if (! srcfn) 90 | srcfn = argv [0]; 91 | else if (! destfn) 92 | destfn = argv [0]; 93 | else 94 | fatal (1, NULL); 95 | } 96 | 97 | if (! srcfn) 98 | fatal (1, NULL); 99 | 100 | buf = malloc (MAX_REC_LEN); 101 | if (! buf) 102 | fatal (2, "can't allocate buffer\n"); 103 | 104 | src = opentape (srcfn, 0, 0); 105 | if (! src) 106 | fatal (3, "can't open source tape\n"); 107 | 108 | if (destfn) 109 | { 110 | dest = opentape (destfn, 1, 1); 111 | if (! dest) 112 | fatal (4, "can't open dest tape\n"); 113 | } 114 | else 115 | verbose++; 116 | 117 | for (;;) 118 | { 119 | len = getrec (src, buf, MAX_REC_LEN); 120 | if ((lencount != 0) && ((len == 0) || (len != prevlen))) 121 | { 122 | if (verbose) 123 | { 124 | if (lencount == 1) 125 | printf ("1 record (%d)\n", firstrec); 126 | else 127 | printf ("%d records (%d..%d)\n", lencount, firstrec, 128 | firstrec+lencount-1); 129 | fflush (stdout); 130 | } 131 | filebytes += prevlen * lencount; 132 | firstrec += lencount; 133 | prevlen = -1; 134 | lencount = 0; 135 | } 136 | if (len != 0) 137 | { 138 | if (lencount == 0) 139 | { 140 | if (verbose) 141 | { 142 | printf ("file %d record length %d: ", file, len); 143 | fflush (stdout); 144 | } 145 | } 146 | if (destfn) 147 | putrec (dest, buf, len); 148 | lencount++; 149 | } 150 | else 151 | { 152 | tapebytes += filebytes; 153 | if (verbose) 154 | { 155 | if (prevlen == 0) 156 | printf ("end of tape, %lu total bytes\n", tapebytes); 157 | else 158 | printf ("end of file %d, %lu bytes\n", file, filebytes); 159 | fflush (stdout); 160 | } 161 | if (destfn) 162 | tapemark (dest); 163 | if (prevlen == 0) 164 | break; 165 | file++; 166 | lencount = 0; 167 | firstrec = 0; 168 | filebytes = 0; 169 | } 170 | prevlen = len; 171 | } 172 | 173 | closetape (src); 174 | if (destfn) 175 | closetape (dest); 176 | 177 | return (0); 178 | } 179 | -------------------------------------------------------------------------------- /tapedump.c: -------------------------------------------------------------------------------- 1 | /* 2 | tapedump 3 | 4 | Copyright 1999, 2000 Eric Smith 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License version 2 as published 8 | by the Free Software Foundation. Note that permission is not granted 9 | to redistribute this program under the terms of any other version of the 10 | General Public License. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #include "stdio.h" 22 | #include "stdarg.h" 23 | #include "stdlib.h" 24 | 25 | #include "tapeio.h" 26 | 27 | #define MAX_REC_LEN 32768 28 | 29 | 30 | typedef unsigned int u32; /* non-portable!!! */ 31 | typedef unsigned char uchar; 32 | 33 | 34 | char *progname; 35 | 36 | void print_usage (FILE *f) 37 | { 38 | fprintf (f, "Usage: %s in\n", progname); 39 | } 40 | 41 | void fatal (int retval, char *fmt, ...) 42 | { 43 | va_list ap; 44 | 45 | if (fmt) 46 | { 47 | fprintf (stderr, "%s: ", progname); 48 | va_start (ap, fmt); 49 | vfprintf (stderr, fmt, ap); 50 | va_end (ap); 51 | } 52 | 53 | if (retval == 1) 54 | print_usage (stderr); 55 | 56 | exit (retval); 57 | } 58 | 59 | 60 | #define BYTES_PER_LINE 16 61 | 62 | void dump (FILE *f, uchar *buf, u32 len) 63 | { 64 | u32 i, j; 65 | 66 | for (i = 0; i < len; i += BYTES_PER_LINE) 67 | { 68 | fprintf (f, " %04x: ", i); 69 | for (j = 0; j < BYTES_PER_LINE; j++) 70 | if ((i + j) < len) 71 | fprintf (f, "%02x ", 0xff & buf [i+j]); 72 | else 73 | fprintf (f, " "); 74 | for (j = 0; j < BYTES_PER_LINE; j++) 75 | if ((i + j) < len) 76 | { 77 | uchar c = buf [i+j] & 0x7f; 78 | if ((c >= ' ') && (c <= '~')) 79 | fprintf (f, "%c", c); 80 | else 81 | fprintf (f, "."); 82 | } 83 | else 84 | fprintf (f, " "); 85 | fprintf (f, "\n"); 86 | } 87 | } 88 | 89 | 90 | #ifdef HP_2000_SUPPORT 91 | 92 | #define MAX_FN_LEN 6 93 | 94 | u32 get16 (uchar *buf, u32 offset) 95 | { 96 | return ((buf [offset] & 0xff) << 8) | (buf [offset+1] & 0xff); 97 | } 98 | 99 | char * file_flag_names [16] = 100 | { 101 | "unrestricted", 102 | "protected", 103 | "locked", 104 | "private", 105 | "unused", 106 | "unused", 107 | "unused", 108 | "unused", 109 | "unused", 110 | "unused", 111 | "unused", 112 | "fcp", 113 | "mwa", 114 | "pfa", 115 | "output", 116 | "input" 117 | }; 118 | 119 | void dump_hp_2000_directory_entry (FILE *f, uchar *buf) 120 | { 121 | u32 uid; 122 | u32 flags; 123 | char uids [6]; 124 | char filename [MAX_FN_LEN + 1]; 125 | int i; 126 | 127 | uid = get16 (buf, 0); 128 | if (uid == 0xffff) 129 | { 130 | fprintf (f, "End of directory\n"); 131 | return; 132 | } 133 | uids [0] = (uid / 1024) + '@'; 134 | sprintf (& uids [1], "%03d", uid & 0x3ff); 135 | for (i = 0; i < MAX_FN_LEN; i++) 136 | filename [i] = buf [i+2] & 0x7f; 137 | filename [sizeof (filename) - 1] = '\0'; 138 | fprintf (f, "ID %s filename '%s'", uids, filename); 139 | if (buf [2] & 0x80) 140 | fprintf (f, " ASCII"); 141 | if (buf [4] & 0x80) 142 | fprintf (f, " file"); 143 | if (buf [2] & 0x80) 144 | fprintf (f, " semi-compiled"); 145 | flags = get16 (buf, 14); 146 | for (i = 15; i >= 0; i--) 147 | { 148 | if (flags & (1 << i)) 149 | fprintf (f, " %s", file_flag_names [i]); 150 | } 151 | fprintf (f, "\n"); 152 | } 153 | 154 | void dump_hp_2000_hibernate_file_header (FILE *f, uchar *buf, u32 len) 155 | { 156 | if (len < 24) 157 | { 158 | fprintf (f, "file header too short for directory entry\n"); 159 | return; 160 | } 161 | 162 | dump_hp_2000_directory_entry (f, buf); 163 | } 164 | 165 | void dump_hp_2000_hibernate (FILE *f, u32 file, u32 record, uchar *buf, u32 len) 166 | { 167 | if ((file > 0) && (record == 0)) 168 | dump_hp_2000_hibernate_file_header (f, buf, len); 169 | } 170 | 171 | 172 | static int mcp_file_type = 0; 173 | 174 | void dump_hp_2000_mcp_file_header (FILE *f, uchar *buf, u32 len) 175 | { 176 | u32 checksum = 0; 177 | u32 info; 178 | int i; 179 | 180 | if (len != 10) 181 | { 182 | fprintf (f, "wrong MCP file header length\n"); 183 | return; 184 | } 185 | if ((get16 (buf, 0) != 01000) || (get16 (buf, 2) != 02001)) 186 | { 187 | fprintf (f, "wrong MCP file header magic numbers\n"); 188 | return; 189 | } 190 | fprintf (f, "MCP file ID word %d ", get16 (buf, 4)); 191 | info = get16 (buf, 6); 192 | switch (info) 193 | { 194 | case 0x0000: 195 | fprintf (f, "abs"); 196 | break; 197 | case 0x8000: 198 | fprintf (f, "rel"); 199 | break; 200 | default: 201 | fprintf (f, "info word %d", info); 202 | break; 203 | } 204 | mcp_file_type = info; 205 | for (i = 2; i < 8; i += 2) 206 | checksum += get16 (buf, i); 207 | checksum &= 0xffff; 208 | if (checksum != get16 (buf, 8)) 209 | fprintf (f, ", bad MCP file header checksum"); 210 | fprintf (f, "\n"); 211 | } 212 | 213 | void dump_hp_2000_abs_record (FILE *f, uchar *buf, u32 len) 214 | { 215 | u32 l; 216 | u32 checksum = 0; 217 | u32 tape_checksum; 218 | u32 i; 219 | 220 | l= buf [0] * 2 + 6; 221 | if ((l != len) || (buf [1] != 0)) 222 | { 223 | fprintf (f, "first word of record is not length (expected %d)\n", l); 224 | return; 225 | } 226 | for (i = 2; i < len - 2; i += 2) 227 | checksum += get16 (buf, i); 228 | checksum &= 0xffff; 229 | tape_checksum = get16 (buf, len - 2); 230 | if (checksum != tape_checksum) 231 | fprintf (f, "computed checksum %04x, tape checksum %04x\n", checksum, 232 | tape_checksum); 233 | } 234 | 235 | void dump_hp_2000_rel_record (FILE *f, uchar *buf, u32 len) 236 | { 237 | u32 l; 238 | 239 | l= buf [0] * 2; 240 | if ((l != len) || (buf [1] != 0)) 241 | { 242 | fprintf (f, "first word of record is not length (expected %d)\n", l); 243 | return; 244 | } 245 | } 246 | 247 | void dump_hp_2000_mcp (FILE *f, u32 file, u32 record, uchar *buf, u32 len) 248 | { 249 | if ((len == 10) && (buf [0] == 0x02) && (buf [1] == 0x00) && 250 | (buf [2] == 0x04) && (buf [3] == 0x01)) 251 | { 252 | dump_hp_2000_mcp_file_header (f, buf, len); 253 | } 254 | else switch (mcp_file_type) 255 | { 256 | case 0x0000: 257 | dump_hp_2000_abs_record (f, buf, len); 258 | break; 259 | case 0x8000: 260 | dump_hp_2000_rel_record (f, buf, len); 261 | break; 262 | default: 263 | fprintf (f, "unknown file type\n"); 264 | } 265 | } 266 | 267 | #endif /* HP_2000_SUPPORT */ 268 | 269 | 270 | typedef enum { 271 | generic, 272 | #ifdef HP_2000_SUPPORT 273 | hp_2000_hibernate, 274 | hp_2000_mcp, 275 | #endif /* HP_2000_SUPPORT */ 276 | } t_tape_type; 277 | 278 | 279 | int main (int argc, char *argv[]) 280 | { 281 | u32 file = 0; 282 | u32 record = 0; 283 | u32 filebytes = 0; 284 | u32 tapebytes = 0; 285 | u32 len; 286 | char *srcfn = NULL; 287 | tape_handle_t src = NULL; 288 | uchar *buf; 289 | t_tape_type tape_type = generic; 290 | int tape_flags = TF_DEFAULT; 291 | 292 | progname = argv [0]; 293 | 294 | while (++argv, --argc) 295 | { 296 | if ((argv [0][0] == '-') && (argv [0][1] != '\0')) 297 | { 298 | #ifdef HP_2000_SUPPORT 299 | if (argv [0][1] == 'h') 300 | tape_type = hp_2000_hibernate; 301 | else if (argv [0][1] == 'm') 302 | tape_type = hp_2000_mcp; 303 | else 304 | #endif /* HP_2000_SUPPORT */ 305 | if (argv [0][1] == 's') 306 | tape_flags |= TF_SIMH; 307 | else 308 | fatal (1, "unrecognized option '%s'\n", argv [0]); 309 | } 310 | else if (! srcfn) 311 | srcfn = argv [0]; 312 | else 313 | fatal (1, NULL); 314 | } 315 | 316 | if (! srcfn) 317 | fatal (1, NULL); 318 | 319 | buf = (uchar *) malloc (MAX_REC_LEN); 320 | if (! buf) 321 | fatal (2, "can't allocate buffer\n"); 322 | 323 | src = opentape (srcfn, 0, 0); 324 | if (! src) 325 | fatal (3, "can't open source tape\n"); 326 | 327 | tapeflags (src, tape_flags); 328 | 329 | for (;;) 330 | { 331 | len = getrec (src, buf, MAX_REC_LEN); 332 | if (len == 0) 333 | { 334 | if (filebytes == 0) 335 | { 336 | printf ("end of tape\n"); 337 | break; 338 | } 339 | 340 | printf ("total length of file %d = %d records, %u bytes\n", 341 | file, record, filebytes); 342 | tapebytes += filebytes; 343 | file++; 344 | record = 0; 345 | filebytes = 0; 346 | printf ("start of file %d\n", file); 347 | fflush (stdout); 348 | } 349 | else 350 | { 351 | printf ("file %d record %d: length %d\n", file, record, len); 352 | switch (tape_type) 353 | { 354 | #ifdef HP_2000_SUPPORT 355 | case hp_2000_hibernate: 356 | dump_hp_2000_hibernate (stdout, file, record, buf, len); 357 | break; 358 | case hp_2000_mcp: 359 | dump_hp_2000_mcp (stdout, file, record, buf, len); 360 | break; 361 | #endif /* HP_2000_SUPPORT */ 362 | default: 363 | break; 364 | } 365 | dump (stdout, buf, len); 366 | fflush (stdout); 367 | filebytes += len; 368 | record++; 369 | } 370 | } 371 | 372 | closetape (src); 373 | 374 | return (0); 375 | } 376 | -------------------------------------------------------------------------------- /tapeio.c: -------------------------------------------------------------------------------- 1 | /* 2 | Routines to do magtape I/O to local tapes, remote tapes, and tape image 3 | files. 4 | 5 | Copyright 1998, 1999 John Wilson and Eric Smith 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License version 2 as published 9 | by the Free Software Foundation. Note that permission is not granted 10 | to redistribute this program under the terms of any other version of the 11 | General Public License. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program. If not, see . 20 | 21 | 08/10/93 JMBW IBM mainframe TCP socket stuff (was using many files). 22 | 07/08/94 JMBW Local magtape code. 23 | 03/13/95 JMBW Converted to separate routines. 24 | 07/19/98 JMBW Added support for "rmt" remote tape protocol. 25 | 11/16/98 ELS Provide struct for per-instance variables. 26 | 02/06/98 ELS Reorganization, and added skiprec and skipfile. 27 | */ 28 | 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include /* for lseek() SEEK_SET, SEEK_END under Linux */ 41 | #include 42 | 43 | #ifdef _AIX /* maybe this will be enough to make it compile on AIX */ 44 | #include 45 | #define MTWEOF STWEOF 46 | #define MTREW STREW 47 | #define MTFSR STFSR 48 | #define MTFSF STFSF 49 | #define MTBSR STRSR 50 | #define MTIOCTOP STIOCTOP 51 | /* not sure about these two (SCSI only): */ 52 | #define MTSETBLK STSETBLK 53 | #define MTSETDENSITY STSETDENSITY 54 | #define mtop stop 55 | #elif __APPLE__ 56 | #define MTWEOF 0 57 | #define MTREW 0 58 | #define MTFSR 0 59 | #define MTFSF 0 60 | #define MTBSR 0 61 | #define MTIOCTOP 0 62 | #define MTSETBLK 0 63 | #define MTSETDENSITY 0 64 | struct mtop { int mt_op; int mt_count; }; 65 | #else 66 | #include 67 | #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) 68 | #define MTSETBLK MTSETBSIZ 69 | #define MTSETDENSITY MTSETDNSTY 70 | #endif 71 | #endif 72 | 73 | #include "tapeio.h" 74 | 75 | 76 | #define TT_UNK 0 77 | #define TT_TAPE 1 /* honest to god tape drive */ 78 | #define TT_IMAGE 2 /* file containing image of a tape */ 79 | #define TT_RMT 3 /* rmt tape server */ 80 | 81 | 82 | struct mtape_t 83 | { 84 | int tape_type; 85 | int tapefd; /* tape drive, file, or socket file descriptor */ 86 | int seek_ok; 87 | int flags; 88 | 89 | unsigned long bpi; /* tape density (for tape length msg) */ 90 | int waccess; /* NZ => tape opened for write access access */ 91 | unsigned long count; /* count of frames written to tape */ 92 | 93 | char netbuf[80]; /* buffer for net commands and responses */ 94 | }; 95 | 96 | 97 | #ifndef O_BINARY 98 | #define O_BINARY 0 99 | #endif 100 | 101 | /* default tape drive device name */ 102 | #define TAPE "/dev/nst0" 103 | /* default tape density */ 104 | #define BPI 1600 105 | 106 | 107 | /* magtape commands */ 108 | static struct mtop mt_weof={ MTWEOF, 1 }; /* operation, count */ 109 | static struct mtop mt_rew={ MTREW, 1 }; 110 | static struct mtop mt_fsr={ MTFSR, 1 }; 111 | static struct mtop mt_fsf={ MTFSF, 1 }; 112 | static struct mtop mt_bsr={ MTBSR, 1 }; 113 | /* SCSI only: */ 114 | static struct mtop mt_setblk={ MTSETBLK, 0 }; /* blockize = 0 (variable) */ 115 | static struct mtop mt_setden={ MTSETDENSITY, 0x02 }; /* density = 1600 */ 116 | 117 | 118 | #define FAIL(msg) do { fprintf (stderr, msg); goto fail; } while (0) 119 | 120 | 121 | /* do a write and check the return status, punt on error */ 122 | static void dowrite (int handle, void *buf, int len) 123 | { 124 | if (write (handle, buf, len) != len) 125 | { 126 | perror ("?Error on write"); 127 | exit (1); 128 | } 129 | } 130 | 131 | 132 | /* do a read and keep trying until we get all bytes */ 133 | static void doread (int handle, void *buf, int len) 134 | { 135 | int n; 136 | while(len) 137 | { 138 | if ((n = read (handle, buf, len)) < 0) 139 | { 140 | perror("?Error on read"); 141 | exit (1); 142 | } 143 | if (n == 0) 144 | { 145 | fprintf (stderr, "?Unexpected end of file\n"); 146 | exit (1); 147 | } 148 | buf += n; 149 | len -= n; 150 | } 151 | } 152 | 153 | 154 | /* get response from "rmt" server */ 155 | static int response (tape_handle_t mtape) 156 | { 157 | char c, rc; 158 | int n; 159 | 160 | doread (mtape->tapefd, &rc, 1); /* get success/error code */ 161 | if (rc != 'A' && rc != 'E') 162 | { /* must be Acknowledge or Error */ 163 | fprintf (stderr, "?Invalid rmt response code: %c\n",rc); 164 | exit (1); 165 | } 166 | 167 | /* get numeric value (returned by both A and E responses) */ 168 | for (n=0;;) 169 | { 170 | doread (mtape->tapefd, &c, 1); /* get next digit */ 171 | if (c < '0' || c > '9') 172 | break; /* not a digit */ 173 | n = n * 10 + (c - '0'); /* add new digit in */ 174 | /* ideally would check for overflow */ 175 | } 176 | if (c != '\n') 177 | { /* first non-digit char must be */ 178 | fprintf (stderr, "?Invalid rmt response terminator: %3.3o\n", 179 | ((int) c) & 0377); 180 | exit (1); 181 | } 182 | if (rc == 'A') 183 | return (n); /* success, return value >=0 */ 184 | /* (unless overflowed) */ 185 | do 186 | doread (mtape->tapefd, &c, 1); 187 | while (c != '\n'); /* ignore until next LF */ 188 | errno = n; /* set error number */ 189 | return (-1); 190 | } 191 | 192 | 193 | /* send ioctl() command to local or remote tape drive */ 194 | static int doioctl (tape_handle_t mtape, struct mtop *op) 195 | { 196 | int len; 197 | 198 | if (mtape->tape_type == TT_TAPE) 199 | return (ioctl (mtape->tapefd, MTIOCTOP, op)); 200 | else 201 | { /* "rmt" tape server */ 202 | /* form cmd (better hope remote MT_OP values are the same) */ 203 | len = sprintf (mtape->netbuf, "I%d\n%d\n", op->mt_op, op->mt_count); 204 | dowrite (mtape->tapefd, mtape->netbuf, len); 205 | return (response (mtape)); 206 | } 207 | } 208 | 209 | 210 | /* open the tape drive (or whatever) */ 211 | /* "create" =1 to create if file, "writable" =1 to open with write access */ 212 | tape_handle_t opentape (char *name, int create, int writable) 213 | { 214 | char *p, *user, *port; 215 | int len; 216 | char *host = NULL; 217 | 218 | tape_handle_t mtape = NULL; 219 | 220 | mtape = (tape_handle_t) calloc (1, sizeof (struct mtape_t)); 221 | if (! mtape) 222 | FAIL ("?can't allocate mtape struct\n"); 223 | 224 | mtape->bpi = BPI; 225 | 226 | mtape->waccess = writable; /* remember if we're writing */ 227 | mtape->count = 0; /* nothing transferred yet */ 228 | 229 | /* get tape filename */ 230 | if (name == NULL) 231 | name = getenv("TAPE"); /* get from environment */ 232 | if (name == NULL) 233 | name = TAPE; /* or use our default */ 234 | 235 | /* just a file if no colon in filename */ 236 | if ((p = index (name, ':')) == NULL) 237 | { 238 | /* there's probably a better way to handle this, in case a file is really 239 | a link to a tape drive -- handler index or something? */ 240 | if (strncmp (name, "/dev/", 5) == 0) 241 | { 242 | /* assume tape if starts with /dev/ */ 243 | mtape->tape_type = TT_TAPE; 244 | mtape->tapefd = open (name, (writable ? O_RDWR : O_RDONLY), 0); 245 | } 246 | else 247 | { /* otherwise file */ 248 | mtape->tape_type = TT_IMAGE; 249 | if (strcmp (name, "-") ==0 ) 250 | { /* stdin/stdout */ 251 | if (writable) 252 | mtape->tapefd=1; 253 | else 254 | mtape->tapefd=0; 255 | } 256 | else 257 | { 258 | if (create) 259 | mtape->tapefd = open (name, O_CREAT | O_TRUNC | 260 | O_WRONLY | O_BINARY, 0644); 261 | else 262 | { 263 | mtape->tapefd = open (name, (writable ? O_RDWR : O_RDONLY) | 264 | O_BINARY, 0); 265 | mtape->seek_ok = 1; 266 | } 267 | } 268 | } 269 | if (mtape->tapefd < 0) 270 | FAIL ("?can't open device or file\n"); 271 | } 272 | else 273 | { /* "rmt" tape server on remote host */ 274 | mtape->tape_type = TT_RMT; 275 | /* split filename around ':' */ 276 | len = p-name; 277 | port = p+1; 278 | 279 | /* can't necessarily modify tape[] so copy it first */ 280 | if ((host = malloc (len + 1)) == NULL) 281 | FAIL ("?can't allocate string for hostname\n"); 282 | strncpy (host, name, len); /* copy hostname */ 283 | host [len] = 0; /* tack on null */ 284 | 285 | /* connect to "rexec" server */ 286 | if ((p = index (host, '@')) == NULL) 287 | { 288 | p = host; /* no @, point at hostname */ 289 | user = NULL; 290 | } 291 | else 292 | { 293 | *p++ = '\0'; /* shoot out @, point at host name */ 294 | user = (*p != '\0') ? host : NULL; /* keep non-null user */ 295 | } 296 | #if !defined(__APPLE__) && !defined(__OpenBSD__) 297 | if ((mtape->tapefd = rexec (&p, htons (512), user, NULL, "/etc/rmt", 298 | (int *) NULL)) < 0) 299 | FAIL ("?Connection failed\n"); 300 | #endif 301 | 302 | /* build rmt "open device" command */ 303 | if ((1 + strlen (port) + 1 + 1 + 1 + 1) > sizeof (mtape->netbuf)) 304 | FAIL ("?Device name too long\n"); 305 | len = sprintf (mtape->netbuf, "O%s\n%d\n", port, writable ? O_RDWR : O_RDONLY); 306 | dowrite (mtape->tapefd, mtape->netbuf, len); 307 | if (response (mtape) < 0) 308 | FAIL ("?Error opening tape drive"); 309 | } 310 | 311 | /* SCSI setup for local/remote tape drive */ 312 | if ((mtape->tape_type == TT_TAPE) || 313 | (mtape->tape_type == TT_RMT)) 314 | { 315 | /* (ignore errors in case not SCSI) */ 316 | /* set variable record length mode */ 317 | doioctl (mtape, & mt_setblk); 318 | /* set density to 1600 */ 319 | doioctl (mtape, & mt_setden); 320 | } 321 | 322 | if (host) 323 | free (host); 324 | 325 | return (mtape); 326 | 327 | fail: 328 | if (mtape) 329 | { 330 | if (host) 331 | free (host); 332 | free (mtape); 333 | } 334 | return (NULL); 335 | } 336 | 337 | 338 | /* close the tape drive */ 339 | void closetape (tape_handle_t mtape) 340 | { 341 | if (mtape->waccess) 342 | { /* opened for create/append */ 343 | tapemark (mtape); /* add one more tape mark */ 344 | /* (should have one already) */ 345 | } 346 | if (mtape->tape_type == TT_RMT) 347 | { 348 | dowrite (mtape->tapefd, "C\n", 2); 349 | if (response (mtape) < 0) 350 | { 351 | perror("?Error closing remote tape"); 352 | exit(1); 353 | } 354 | } 355 | if (close (mtape->tapefd) < 0) 356 | { 357 | perror("?Error closing tape"); 358 | exit(1); 359 | } 360 | free (mtape); 361 | } 362 | 363 | 364 | /* rewind tape */ 365 | void posnbot (tape_handle_t mtape) 366 | { 367 | if (mtape->tape_type == TT_IMAGE) 368 | { /* image file */ 369 | if (lseek (mtape->tapefd, 0L, SEEK_SET) < 0) 370 | { 371 | perror("?Seek failed"); 372 | exit(1); 373 | } 374 | } 375 | else 376 | { /* local/remote tape drive */ 377 | if (doioctl (mtape, & mt_rew) < 0) 378 | { 379 | perror("?Rewind failed"); 380 | exit(1); 381 | } 382 | } 383 | } 384 | 385 | 386 | /* position tape at EOT (between the two tape marks) */ 387 | void posneot (tape_handle_t mtape) 388 | { 389 | if (mtape->tape_type == TT_IMAGE) 390 | { /* image file */ 391 | if (lseek (mtape->tapefd, -4L, SEEK_END) < 0) 392 | { 393 | perror("?Seek failed"); 394 | exit(1); 395 | } 396 | } 397 | else 398 | { /* local/remote tape drive */ 399 | doioctl (mtape, & mt_bsr); /* in case already at LEOT */ 400 | while (1) 401 | { 402 | /* space forward a file */ 403 | if (doioctl (mtape, & mt_fsf) < 0) 404 | { 405 | perror("?Error spacing to EOT"); 406 | exit(1); 407 | } 408 | /* space one record more to see if double EOF */ 409 | if (doioctl (mtape, & mt_fsr) < 0) 410 | break; 411 | /* might want to check errno to make sure it's the right error */ 412 | } 413 | #if 1 414 | /* "man mtio" doesn't say whether MTFSR actually moves past */ 415 | /* the tape mark, let's assume it does */ 416 | if (doioctl (mtape, & mt_bsr) < 0) 417 | { /* get between them */ 418 | perror("?Error backspacing at EOT"); 419 | exit(1); 420 | } 421 | #endif 422 | } 423 | } 424 | 425 | 426 | /* read a tape record, return actual length (0=tape mark) */ 427 | int getrec (tape_handle_t mtape, void *buf, int len) 428 | { 429 | unsigned char byte [4]; /* 32 bits for length field(s) */ 430 | unsigned long l; /* at least 32 bits */ 431 | int i; 432 | 433 | if (mtape->tape_type == TT_IMAGE) 434 | { /* image file */ 435 | doread (mtape->tapefd, byte, 4); /* get record length */ 436 | l=((unsigned long)byte[3]<<24L)|((unsigned long)byte[2]<<16L)| 437 | ((unsigned long)byte[1]<<8L)|(unsigned long)byte[0]; 438 | /* compose into longword */ 439 | if (l > len) 440 | goto toolong; /* don't read if too long for buf */ 441 | if (l != 0) 442 | { /* get data unless tape mark */ 443 | char x; 444 | doread (mtape->tapefd, buf, l); /* read data */ 445 | if ((l & 1) != 0 && (mtape->flags & TF_SIMH) != 0) 446 | doread (mtape->tapefd, &x, 1); 447 | doread (mtape->tapefd, byte, 4); /* get trailing record length */ 448 | if((((unsigned long)byte[3]<<24L)| 449 | ((unsigned long)byte[2]<<16L)| 450 | ((unsigned long)byte[1]<<8)| 451 | (unsigned long)byte[0])!=l) 452 | { /* should match */ 453 | fprintf (stderr,"?Corrupt tape image\n"); 454 | exit(1); 455 | } 456 | } 457 | } 458 | else if (mtape->tape_type == TT_RMT) 459 | { /* rmt tape server */ 460 | len = sprintf (mtape->netbuf, "R%d\n", len); 461 | dowrite (mtape->tapefd, mtape->netbuf, len); 462 | if ((i = response (mtape)) < 0) 463 | { 464 | perror("?Error reading tape"); 465 | exit(1); 466 | } 467 | l = i; 468 | if (l) 469 | doread (mtape->tapefd, buf, l); 470 | } 471 | else 472 | { /* local tape drive */ 473 | if ((i = read (mtape->tapefd, buf, len)) < 0) 474 | { 475 | perror("?Error reading tape"); 476 | exit(1); 477 | } 478 | l = i; 479 | } 480 | return(l); 481 | 482 | toolong: 483 | fprintf(stderr,"?%ld byte tape record too long for %d byte buffer\n", 484 | l,len); 485 | exit(1); 486 | } 487 | 488 | 489 | /* write a tape record */ 490 | void putrec (tape_handle_t mtape, void *buf, int len) 491 | { 492 | unsigned char l [4]; 493 | 494 | if (mtape->tape_type == TT_IMAGE) 495 | { /* image file */ 496 | l [0] = len & 0377; /* PDP-11 byte order */ 497 | l [1] = (len >> 8) &0377; 498 | l [2] = 0; /* our recs are always < 64 KB */ 499 | l [3] = 0; 500 | dowrite (mtape->tapefd, l, 4); /* write longword length */ 501 | dowrite (mtape->tapefd, buf, len); /* write data */ 502 | dowrite (mtape->tapefd, l, 4); /* write length again */ 503 | } 504 | else if (mtape->tape_type == TT_RMT) 505 | { /* rmt tape */ 506 | int n; 507 | n = sprintf (mtape->netbuf, "W%d\n", len); 508 | dowrite (mtape->tapefd, mtape->netbuf, n); 509 | dowrite (mtape->tapefd, buf, len); 510 | } 511 | else 512 | dowrite (mtape->tapefd, buf, len); /* just write the data if tape */ 513 | 514 | mtape->count += len + (mtape->bpi * 3 /5); /* add to byte count 515 | (+0.6" tape gap) */ 516 | } 517 | 518 | 519 | /* write a tape mark */ 520 | void tapemark (tape_handle_t mtape) 521 | { 522 | static char zero [4] = { 0, 0, 0, 0 }; 523 | 524 | if (mtape->tape_type == TT_IMAGE) 525 | { /* image file */ 526 | dowrite (mtape->tapefd, zero, 4); /* write longword length */ 527 | } 528 | else 529 | { /* local/remote tape drive */ 530 | if (doioctl (mtape, & mt_weof) < 0) 531 | { 532 | perror ("?Failed writing tape mark"); 533 | exit (1); 534 | } 535 | } 536 | mtape->count += 3 * mtape->bpi; /* 3" of tape */ 537 | } 538 | 539 | 540 | /* skip records (negative for reverse) */ 541 | void skiprec (tape_handle_t mtape, int count) 542 | { 543 | unsigned char byte [4]; /* 32 bits for length field(s) */ 544 | unsigned long l; /* at least 32 bits */ 545 | 546 | if (mtape->tape_type != TT_IMAGE) 547 | { 548 | fprintf (stderr, "?Record skip only implemented for image files"); 549 | exit (1); 550 | } 551 | 552 | if (count < 0) 553 | { 554 | fprintf (stderr, "?Record skip reverse not yet implemented"); 555 | exit (1); 556 | } 557 | 558 | while (count--) 559 | { 560 | doread (mtape->tapefd, byte, 4); /* get record length */ 561 | 562 | /* compose into longword */ 563 | l=((unsigned long)byte[3]<<24L)|((unsigned long)byte[2]<<16L)| 564 | ((unsigned long)byte[1]<<8L)|(unsigned long)byte[0]; 565 | 566 | if (l == 0) /* hit tape mark? */ 567 | return; /* note that we've effectively skipped over the tape mark */ 568 | 569 | /* skip record */ 570 | if (lseek (mtape->tapefd, l, SEEK_CUR) < 0) 571 | { 572 | perror ("?Seek failed"); 573 | exit (1); 574 | } 575 | 576 | doread (mtape->tapefd, byte, 4); /* get trailing record length */ 577 | if((((unsigned long)byte[3]<<24L)| 578 | ((unsigned long)byte[2]<<16L)| 579 | ((unsigned long)byte[1]<<8)| 580 | (unsigned long)byte[0])!=l) 581 | { /* should match */ 582 | fprintf (stderr,"?Corrupt tape image\n"); 583 | exit(1); 584 | } 585 | } 586 | } 587 | 588 | 589 | /* skip forward to the next file mark, and leave the tape positioned 590 | after the mark */ 591 | static void skip_to_mark (tape_handle_t mtape) 592 | { 593 | unsigned char byte [4]; /* 32 bits for length field(s) */ 594 | unsigned long l; /* at least 32 bits */ 595 | 596 | static char scratch_buf [4096]; 597 | 598 | if (mtape->tape_type != TT_IMAGE) 599 | { 600 | fprintf (stderr, "?Record skip only implemented for image files"); 601 | exit (1); 602 | } 603 | 604 | for (;;) 605 | { 606 | doread (mtape->tapefd, byte, 4); /* get record length */ 607 | 608 | /* compose into longword */ 609 | l=((unsigned long)byte[3]<<24L)|((unsigned long)byte[2]<<16L)| 610 | ((unsigned long)byte[1]<<8L)|(unsigned long)byte[0]; 611 | 612 | if (l == 0) /* hit tape mark? */ 613 | return; /* note that we've effectively skipped over the tape mark */ 614 | 615 | /* skip record */ 616 | if (mtape->seek_ok) 617 | { 618 | if (lseek (mtape->tapefd, l, SEEK_CUR) < 0) 619 | { 620 | perror ("?Seek failed"); 621 | exit (1); 622 | } 623 | } 624 | else 625 | { 626 | int len, len2; 627 | len = l; 628 | while (len > 0) 629 | { 630 | len2 = len; 631 | if (len2 > sizeof (scratch_buf)) 632 | len2 = sizeof (scratch_buf); 633 | doread (mtape->tapefd, scratch_buf, len2); /* read data */ 634 | len -= len2; 635 | } 636 | } 637 | 638 | doread (mtape->tapefd, byte, 4); /* get trailing record length */ 639 | if((((unsigned long)byte[3]<<24L)| 640 | ((unsigned long)byte[2]<<16L)| 641 | ((unsigned long)byte[1]<<8)| 642 | (unsigned long)byte[0])!=l) 643 | { /* should match */ 644 | fprintf (stderr,"?Corrupt tape image\n"); 645 | exit(1); 646 | } 647 | } 648 | } 649 | 650 | 651 | /* skip files (negative for reverse) */ 652 | void skipfile (tape_handle_t mtape, int count) 653 | { 654 | if (mtape->tape_type != TT_IMAGE) 655 | { 656 | fprintf (stderr, "?File skip only implemented for image files"); 657 | exit (1); 658 | } 659 | 660 | if (count < 0) 661 | { 662 | fprintf (stderr, "?File skip reverse not yet implemented"); 663 | exit (1); 664 | } 665 | 666 | while (count--) 667 | { 668 | skip_to_mark (mtape); 669 | } 670 | } 671 | 672 | /* set tape flags */ 673 | void tapeflags (tape_handle_t mtape, int flags) 674 | { 675 | mtape->flags = flags; 676 | } 677 | -------------------------------------------------------------------------------- /tapeio.h: -------------------------------------------------------------------------------- 1 | /* 2 | Routines to do magtape I/O to local tapes, remote tapes, and tape image 3 | files. 4 | 5 | Copyright 1998, 1999 John Wilson and Eric Smith 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License version 2 as published 9 | by the Free Software Foundation. Note that permission is not granted 10 | to redistribute this program under the terms of any other version of the 11 | General Public License. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program. If not, see . 20 | */ 21 | 22 | 23 | typedef struct mtape_t *tape_handle_t; /* opaque type */ 24 | 25 | 26 | /* tape flags */ 27 | #define TF_DEFAULT 0x000 28 | #define TF_SIMH 0x001 29 | 30 | 31 | /* open a tape drive */ 32 | tape_handle_t opentape (char *name, int create, int writable); 33 | 34 | /* close a tape drive */ 35 | void closetape (tape_handle_t h); 36 | 37 | /* rewind tape */ 38 | void posnbot (tape_handle_t h); 39 | 40 | /* position tape at EOT (between the two tape marks) */ 41 | void posneot (tape_handle_t h); 42 | 43 | /* read a tape record, return actual length (0=tape mark) */ 44 | int getrec (tape_handle_t h, void *buf, int len); 45 | 46 | /* write a tape record */ 47 | void putrec (tape_handle_t h, void *buf, int len); 48 | 49 | /* write a tape mark */ 50 | void tapemark (tape_handle_t h); 51 | 52 | /* skip records (negative for reverse) */ 53 | void skiprec (tape_handle_t h, int count); 54 | 55 | /* skip files (negative for reverse) */ 56 | void skipfile (tape_handle_t h, int count); 57 | 58 | /* set tape flags */ 59 | void tapeflags (tape_handle_t h, int flags); 60 | -------------------------------------------------------------------------------- /taperead.c: -------------------------------------------------------------------------------- 1 | /* 2 | taperead 3 | 4 | Copyright 1999, 2000 Eric Smith 5 | Copyright 2016 Lars Brinkhoff 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License version 2 as published 9 | by the Free Software Foundation. Note that permission is not granted 10 | to redistribute this program under the terms of any other version of the 11 | General Public License. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program. If not, see . 20 | */ 21 | 22 | #include "stdio.h" 23 | #include "stdarg.h" 24 | #include "stdlib.h" 25 | 26 | #include "tapeio.h" 27 | 28 | #define MAX_REC_LEN 32768 29 | 30 | 31 | typedef unsigned int u32; /* non-portable!!! */ 32 | 33 | 34 | char *progname; 35 | int print_verbose = 0; 36 | 37 | void print_usage (FILE *f) 38 | { 39 | fprintf (f, "Usage: %s in\n", progname); 40 | } 41 | 42 | void fatal (int retval, char *fmt, ...) 43 | { 44 | va_list ap; 45 | 46 | if (fmt) 47 | { 48 | fprintf (stderr, "%s: ", progname); 49 | va_start (ap, fmt); 50 | vfprintf (stderr, fmt, ap); 51 | va_end (ap); 52 | } 53 | 54 | if (retval == 1) 55 | print_usage (stderr); 56 | 57 | exit (retval); 58 | } 59 | 60 | 61 | void verbose (char *fmt, ...) 62 | { 63 | va_list ap; 64 | va_start (ap, fmt); 65 | if (print_verbose) 66 | vfprintf (stdout, fmt, ap); 67 | va_end (ap); 68 | } 69 | 70 | 71 | int main (int argc, char *argv[]) 72 | { 73 | u32 file = 0; 74 | u32 record = 0; 75 | u32 filebytes = 0; 76 | u32 tapebytes = 0; 77 | u32 len; 78 | char *srcfn = NULL; 79 | tape_handle_t src = NULL; 80 | FILE *dst = NULL; 81 | char *buf; 82 | int tape_flags = TF_DEFAULT; 83 | char filename[100]; 84 | 85 | progname = argv [0]; 86 | 87 | while (++argv, --argc) 88 | { 89 | if ((argv [0][0] == '-') && (argv [0][1] != '\0')) 90 | { 91 | if (argv [0][1] == 's') 92 | tape_flags |= TF_SIMH; 93 | else if (argv [0][1] == 'v') 94 | print_verbose = 1; 95 | else 96 | fatal (1, "unrecognized option '%s'\n", argv [0]); 97 | } 98 | else if (! srcfn) 99 | srcfn = argv [0]; 100 | else 101 | fatal (1, NULL); 102 | } 103 | 104 | if (! srcfn) 105 | fatal (1, NULL); 106 | 107 | buf = malloc (MAX_REC_LEN); 108 | if (! buf) 109 | fatal (2, "can't allocate buffer\n"); 110 | 111 | src = opentape (srcfn, 0, 0); 112 | if (! src) 113 | fatal (3, "can't open source tape\n"); 114 | 115 | tapeflags (src, tape_flags); 116 | 117 | dst = fopen("file0000", "wb"); 118 | 119 | for (;;) 120 | { 121 | len = getrec (src, buf, MAX_REC_LEN); 122 | if (len == 0) 123 | { 124 | fclose (dst); 125 | 126 | if (filebytes == 0) 127 | { 128 | verbose ("end of tape\n"); 129 | break; 130 | } 131 | 132 | verbose ("total length of file %d = %d records, %u bytes\n", 133 | file, record, filebytes); 134 | tapebytes += filebytes; 135 | file++; 136 | record = 0; 137 | filebytes = 0; 138 | verbose ("start of file %d\n", file); 139 | sprintf (filename, "file%04d", file); 140 | dst = fopen(filename, "wb"); 141 | 142 | fflush (stdout); 143 | } 144 | else 145 | { 146 | verbose ("file %d record %d: length %d\n", file, record, len); 147 | fflush (stdout); 148 | fwrite (buf, 1, len, dst); 149 | fflush (dst); 150 | filebytes += len; 151 | record++; 152 | } 153 | } 154 | 155 | closetape (src); 156 | 157 | return (0); 158 | } 159 | -------------------------------------------------------------------------------- /tapewrite.c: -------------------------------------------------------------------------------- 1 | /* 2 | tapewrite 3 | 4 | Copyright 1999, 2000 Eric Smith 5 | Copyright 2016 Lars Brinkhoff 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License version 2 as published 9 | by the Free Software Foundation. Note that permission is not granted 10 | to redistribute this program under the terms of any other version of the 11 | General Public License. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program. If not, see . 20 | */ 21 | 22 | #include "stdio.h" 23 | #include "stdarg.h" 24 | #include "stdlib.h" 25 | 26 | #include "tapeio.h" 27 | 28 | #define MAX_REC_LEN 32768 29 | 30 | 31 | typedef unsigned int u32; /* non-portable!!! */ 32 | 33 | 34 | char *progname; 35 | int print_verbose = 0; 36 | 37 | void print_usage (FILE *f) 38 | { 39 | fprintf (f, "Usage: %s out files...\n", progname); 40 | } 41 | 42 | void fatal (int retval, char *fmt, ...) 43 | { 44 | va_list ap; 45 | 46 | if (fmt) 47 | { 48 | fprintf (stderr, "%s: ", progname); 49 | va_start (ap, fmt); 50 | vfprintf (stderr, fmt, ap); 51 | va_end (ap); 52 | } 53 | 54 | if (retval == 1) 55 | print_usage (stderr); 56 | 57 | exit (retval); 58 | } 59 | 60 | 61 | void verbose (char *fmt, ...) 62 | { 63 | va_list ap; 64 | va_start (ap, fmt); 65 | if (print_verbose) 66 | vfprintf (stdout, fmt, ap); 67 | va_end (ap); 68 | } 69 | 70 | 71 | int main (int argc, char *argv[]) 72 | { 73 | u32 file = 0; 74 | u32 record = 0; 75 | u32 recordlen = 1024; 76 | u32 filebytes = 0; 77 | u32 tapebytes = 0; 78 | u32 len; 79 | tape_handle_t dst = NULL; 80 | FILE *src = NULL; 81 | char *buf; 82 | int tape_flags = TF_DEFAULT; 83 | 84 | progname = argv [0]; 85 | 86 | while (++argv, --argc) 87 | { 88 | if ((argv [0][0] == '-') && (argv [0][1] != '\0')) 89 | { 90 | if (argv [0][1] == 's') 91 | tape_flags |= TF_SIMH; 92 | else if (argv [0][1] == 'v') 93 | print_verbose = 1; 94 | else if (argv [0][1] == 'n') 95 | { 96 | ++argv, --argc; 97 | recordlen = atoi(argv [0]); 98 | } 99 | else 100 | fatal (1, "unrecognized option '%s'\n", argv [0]); 101 | } 102 | else 103 | { 104 | dst = opentape (argv [0], 1, 1); 105 | if (! dst) 106 | fatal (3, "can't open destination tape\n"); 107 | break; 108 | } 109 | } 110 | 111 | buf = malloc (MAX_REC_LEN); 112 | if (! buf) 113 | fatal (2, "can't allocate buffer\n"); 114 | 115 | tapeflags (dst, tape_flags); 116 | 117 | while (++argv, --argc) 118 | { 119 | src = fopen(argv [0], "rb"); 120 | file++; 121 | verbose ("reading from file %s\n", argv [0]); 122 | for (;;) 123 | { 124 | len = fread (buf, 1, recordlen, src); 125 | verbose ("read %u bytes\n", len); 126 | if (len > 0) 127 | { 128 | putrec (dst, buf, recordlen); 129 | tapebytes += recordlen; 130 | filebytes += recordlen; 131 | record++; 132 | } 133 | if (len < recordlen) 134 | { 135 | verbose ("end of file, %u records, %u bytes\n", record, filebytes); 136 | fclose (src); 137 | tapemark (dst); 138 | record = 0; 139 | filebytes = 0; 140 | break; 141 | } 142 | } 143 | } 144 | 145 | closetape (dst); 146 | verbose ("end of tape, %u files, %u bytes\n", file, tapebytes); 147 | 148 | return (0); 149 | } 150 | -------------------------------------------------------------------------------- /tapex.c: -------------------------------------------------------------------------------- 1 | /* 2 | Dump contents of TAPEX tapes, or tape images. Based on Eric Smith's 3 | modified t10backup.c. 4 | 5 | Copyright 1999 Eric Smith 6 | Copyright 2017 Lars Brinkhoff 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License version 2 as published 10 | by the Free Software Foundation. Note that permission is not granted 11 | to redistribute this program under the terms of any other version of the 12 | General Public License. 13 | 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License 20 | along with this program; if not, write to the Free Software 21 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22 | */ 23 | 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "tapeio.h" 35 | 36 | #define bool long 37 | #define false 0 38 | #define true 1 39 | 40 | #define MAX_FILE_SPEC 1024 41 | 42 | #define RAWSIZE (5*(512)) 43 | 44 | #define endof(s) (strchr(s, (char) 0)) 45 | 46 | char *progname; 47 | 48 | 49 | tape_handle_t tape; /* Source "tape". */ 50 | 51 | 52 | bool eightbit = false; /* Status of -8 (eight-bit) flag. */ 53 | bool buildtree = false; /* Status of -d (build trees) flag. */ 54 | bool interchange = false; /* Status of -i (interchange) flag. */ 55 | long verbose = 0; /* Status of -v (verbose) flag. */ 56 | 57 | 58 | char* argfiles [MAX_FILE_SPEC]; /* File spec's to extract. */ 59 | long argcount; /* Number of them. */ 60 | 61 | 62 | unsigned char rawdata[RAWSIZE]; /* Raw data for a tape block. */ 63 | 64 | long headlh[512], headrh[512]; /* Header block from tape. */ 65 | long datalh[512], datarh[512]; /* Data block from tape. */ 66 | 67 | long prevSEQ; /* SEQ number of previous block. */ 68 | long currentfilenumber; 69 | 70 | long defercount; /* Count of defered output bytes. */ 71 | 72 | bool extracting; 73 | FILE* destination; 74 | 75 | /* Tape information: */ 76 | 77 | char systemname[100]; 78 | char savesetname[100]; 79 | 80 | /* File information: */ 81 | 82 | long a_bsiz; /* For now. */ 83 | long a_alls; 84 | long a_mode; 85 | long a_leng; 86 | 87 | char filedev[100]; /* Device: */ 88 | char filedir[100]; /* [ufd] */ 89 | char filename[1000]; /* file name. */ 90 | char fileext[100]; /* extension. */ 91 | int file_bytesize; 92 | int file_blocks; 93 | long long file_bytes; 94 | 95 | char filespec[7][100]; /* [0]: device:ufd. */ 96 | /* [1-5]: sfd's, stored directly here. */ 97 | /* [6]: file.ext */ 98 | 99 | char cname[100]; /* Canonical name. */ 100 | 101 | 102 | void print_usage (FILE *f) 103 | { 104 | fprintf (f, "Usage: %s [options] [filespecs...]\n", progname); 105 | fprintf (f, "Options:\n" 106 | " -t list directory\n" 107 | " -x extract files\n" 108 | " -f read input from file\n" 109 | " /dev/* tape drive\n" 110 | " host:file rmt server\n" 111 | " file tape image file\n" 112 | " - tape image from stdin\n" 113 | " -s use save sets in range\n" 114 | " e.g. '-s 2' for only save set 2\n" 115 | " '-s 2,' for save set 2 through EOT\n" 116 | " '-s 3,5' for save sets 3 through 5\n" 117 | " -v verbose\n" 118 | " -vv very verbose\n" 119 | " -8 eight bit mode\n" 120 | " -i interchange mode\n" 121 | " -d build directory tree\n"); 122 | } 123 | 124 | 125 | void fatal (int retval, char *fmt, ...) 126 | { 127 | va_list ap; 128 | 129 | if (fmt) 130 | { 131 | fprintf (stderr, "%s: ", progname); 132 | va_start (ap, fmt); 133 | vfprintf (stderr, fmt, ap); 134 | va_end (ap); 135 | } 136 | 137 | if (retval == 1) 138 | print_usage (stderr); 139 | 140 | exit (retval); 141 | } 142 | 143 | 144 | void warning (char *fmt, ...) 145 | { 146 | va_list ap; 147 | 148 | fprintf (stderr, "%s: ", progname); 149 | va_start (ap, fmt); 150 | vfprintf (stderr, fmt, ap); 151 | va_end (ap); 152 | } 153 | 154 | 155 | /* unpackheader unpacks the header block from the raw stream. */ 156 | 157 | void unpackheader (void) 158 | { 159 | unsigned char* rawptr; 160 | long i, left, right; 161 | unsigned char c; 162 | 163 | rawptr = & rawdata [0]; 164 | 165 | for (i = 0; i < 512; i++) 166 | { 167 | left = * (rawptr++) << 10; 168 | left |= * (rawptr++) << 2; 169 | left |= (c = * (rawptr++)) >> 6; 170 | right = (c & 077) << 12; 171 | right |= * (rawptr++) << 4; 172 | right |= * (rawptr++) & 017; 173 | headlh [i] = left; 174 | headrh [i] = right; 175 | } 176 | } 177 | 178 | void unpack_filename(void) 179 | { 180 | long long x; 181 | char *s = filename; 182 | int i, j; 183 | 184 | for (i = 2; i < 512; i++) { 185 | x = headlh[i]; 186 | x <<= 18; 187 | x += headrh[i]; 188 | if (x == 0) 189 | break; 190 | for (j = 0; j < 5; j++) { 191 | *s++ = ((x >> 29) & 0177); 192 | x <<= 7; 193 | } 194 | } 195 | } 196 | 197 | 198 | /* unpackdata unpacks the data block from the raw stream. */ 199 | 200 | void unpackdata (void) 201 | { 202 | unsigned char* rawptr; 203 | long i, left, right; 204 | unsigned char c; 205 | 206 | rawptr = & rawdata [32*5]; 207 | 208 | for (i = 0; i < 512; i++) 209 | { 210 | left = * (rawptr++) << 10; 211 | left |= * (rawptr++) << 2; 212 | left |= (c = * (rawptr++)) >> 6; 213 | right = (c & 077) << 12; 214 | right |= * (rawptr++) << 4; 215 | right |= * (rawptr++) & 017; 216 | datalh [i] = left; 217 | datarh [i] = right; 218 | } 219 | } 220 | 221 | void unpackinfo (void) 222 | { 223 | file_bytesize = (headlh[0] >> 6) & 077; 224 | file_blocks = headrh[0]; 225 | file_bytes = headlh[1]; 226 | file_bytes <<= 18; 227 | file_bytes += headrh[1]; 228 | unpack_filename(); 229 | } 230 | 231 | /* pars_5chars reads five ASCII chars from a machine word. */ 232 | 233 | void pars_5chars (long index, char *store) 234 | { 235 | long l, r; 236 | 237 | l = datalh [index]; 238 | r = datarh [index]; 239 | 240 | store [0] = (0177 & (l >> 11)); 241 | store [1] = (0177 & (l >> 4)); 242 | store [2] = (0177 & ((l << 3) | ((r >> 15) & 017))); 243 | store [3] = (0177 & (r >> 8)); 244 | store [4] = (0177 & (r >> 1)); 245 | } 246 | 247 | 248 | /* pars_asciz stores asciz text from data */ 249 | 250 | void pars_asciz (long index, char *store) 251 | { 252 | long words; 253 | 254 | words = datarh [index++]; 255 | while ((words--) > 0) 256 | { 257 | pars_5chars (index++, store); 258 | store += 5; 259 | } 260 | * store = (char) 0; 261 | } 262 | 263 | 264 | void zerotapeinfo (void) 265 | { 266 | } 267 | 268 | void zerofileinfo (void) 269 | { 270 | file_bytesize = 0; 271 | file_blocks = 0; 272 | file_bytes = 0; 273 | filename[0] = 0; 274 | } 275 | 276 | 277 | void printtapeinfo (void) 278 | { 279 | if (!verbose) 280 | return; 281 | 282 | fprintf (stderr, "%s", filename); 283 | if (verbose > 1) 284 | fprintf (stderr, " %d blocks; %lld %d-bit bytes\n", 285 | file_blocks, file_bytes, file_bytesize); 286 | else 287 | fputc ('\n', stderr); 288 | 289 | if (verbose > 2 && (headlh[0] & 0770077) != 0) 290 | fprintf (stderr, "MYSTERY HEADER: %06lo%06lo %06lo%06lo\n", 291 | headlh[0], headrh[0], headlh[1], headrh[1]); 292 | } 293 | 294 | 295 | void downcase (char *s) 296 | { 297 | while (*s != (char) 0) { 298 | if (isupper(*s)) *s = tolower(*s); 299 | s++; 300 | } 301 | } 302 | 303 | 304 | void buildfilenames (void) 305 | { 306 | long i; 307 | 308 | if (* filedev != (char) 0) 309 | sprintf (filespec [0], "%s:%s", filedev, filedir); 310 | else 311 | sprintf (filespec [0], "%s", filedir); 312 | 313 | sprintf (filespec [6], "%s.%s", filename, fileext); 314 | 315 | for (i = 0; i < 7; i++) 316 | downcase (filespec [i]); 317 | 318 | sprintf (cname, "%s", filespec [0]); 319 | for (i = 1; i < 6; i++) 320 | { 321 | if (* filespec [i] != (char) 0) 322 | sprintf (endof (cname), ".%s", filespec [i]); 323 | } 324 | if (* cname != (char) 0) 325 | sprintf (endof (cname), "..%s", filespec [6]); 326 | else 327 | sprintf (cname, "%s", filespec [6]); 328 | } 329 | 330 | 331 | void printfileinfo (void) 332 | { 333 | buildfilenames (); 334 | printf ("%3ld %s", currentfilenumber, cname); 335 | if (verbose) 336 | { 337 | printf (" (%ld) alloc:%ld, mode:%lo, len:%ld", a_bsiz, a_alls, a_mode, a_leng); 338 | } 339 | printf ("\n"); 340 | } 341 | 342 | 343 | /* readblock reads one logical block from the input stream. */ 344 | /* The header is unpacked into head{l,r}; the data is not. */ 345 | 346 | int readblock (void) 347 | { 348 | long i; 349 | i = getrec (tape, rawdata, RAWSIZE); 350 | if (i == 0) 351 | return (0); 352 | if (i != RAWSIZE) 353 | { 354 | fprintf (stderr, "record length %ld, expected %d\n", i, RAWSIZE); 355 | while (i++ < RAWSIZE) rawdata [i] = (char) 0; 356 | } 357 | return (1); 358 | } 359 | 360 | 361 | /* Disk file output routines: */ 362 | 363 | void WriteBlock (void) 364 | { 365 | } 366 | 367 | 368 | /* OpenOutput opens the output file, according to -d and -i flags. */ 369 | 370 | bool OpenOutput (void) 371 | { 372 | struct stat statbuf; 373 | char oname [100]; 374 | long i; 375 | 376 | defercount = 0; 377 | 378 | if (interchange) 379 | destination = fopen (filespec [6], "w"); 380 | else if (! buildtree) 381 | destination = fopen(cname, "w"); 382 | else 383 | { 384 | for (i = 0, oname [0] = (char) 0; i < 6; i++) 385 | { 386 | if (* filespec [i] == (char) 0) 387 | break; 388 | sprintf (endof (oname), "%s", filespec [i]); 389 | if (stat (oname, & statbuf) != 0) 390 | { 391 | if (mkdir (oname, 0777) != 0) 392 | { 393 | warning ("cannot create %s/\n", oname); 394 | return (false); 395 | } 396 | } 397 | sprintf (endof (oname), "/"); 398 | } 399 | sprintf (endof (oname), "%s", filespec [6]); 400 | destination = fopen (oname, "w"); 401 | } 402 | 403 | return (destination != NULL); 404 | } 405 | 406 | void CloseOutput (void) 407 | { 408 | /* Close output file after us. */ 409 | } 410 | 411 | /* Argmatch checks if the current file matches the given argument: */ 412 | 413 | bool argmatch (char *arg) 414 | { 415 | long target; 416 | char* f; 417 | char* p; 418 | char* s; 419 | 420 | if (*arg == '#') 421 | { 422 | (void) sscanf (arg, "#%ld", & target); 423 | return (target == currentfilenumber); 424 | } 425 | 426 | if (*arg == '*') 427 | return (1); 428 | 429 | for (f = cname; *f != (char) 0; f++) 430 | { 431 | for (p = f, s = arg; (*s != (char) 0) && (*p == *s); p++, s++) 432 | ; 433 | if (*s == (char) 0) 434 | return (true); 435 | } 436 | return (false); 437 | } 438 | 439 | /* doextract performs the job of "tapex -x ..." */ 440 | 441 | int doextract (void) 442 | { 443 | int got_blocks = 0; 444 | long i; 445 | 446 | currentfilenumber = 0; 447 | extracting = false; 448 | 449 | for (;;) 450 | { 451 | if (! readblock ()) 452 | return (got_blocks); 453 | 454 | got_blocks = 1; 455 | 456 | currentfilenumber++; 457 | zerofileinfo (); 458 | buildfilenames (); 459 | for (i = 0; i < argcount; i++) 460 | { 461 | if (argmatch (argfiles [i])) 462 | { 463 | if (*argfiles[i] == '#') 464 | { 465 | /* Maybe do a pure shift here? */ 466 | argfiles [i] = argfiles [--argcount]; 467 | } 468 | extracting = true; 469 | break; 470 | } 471 | } 472 | if (extracting) 473 | { 474 | if (OpenOutput ()) 475 | { 476 | if (verbose) 477 | { 478 | printf ("Extracting %s", cname); 479 | fflush (stdout); 480 | } 481 | } 482 | else 483 | { 484 | warning ("can't open %s for output\n", cname); 485 | extracting = false; 486 | } 487 | } 488 | } 489 | 490 | if (extracting) 491 | { 492 | WriteBlock (); 493 | } 494 | } 495 | 496 | /* dodirectory performs the job of "backup -t ..." */ 497 | 498 | int dodirectory (void) 499 | { 500 | int end_of_tape = 0; 501 | int n; 502 | 503 | for (;;) 504 | { 505 | n = readblock (); 506 | if (n == 0) { 507 | if (end_of_tape) 508 | return 0; 509 | end_of_tape = 1; 510 | continue; 511 | } 512 | 513 | end_of_tape = 0; 514 | unpackheader (); 515 | unpackinfo (); 516 | zerotapeinfo (); 517 | printtapeinfo (); 518 | 519 | while (file_blocks-- > 0) 520 | { 521 | readblock (); 522 | } 523 | } 524 | } 525 | 526 | /* command decoder and dispatcher */ 527 | 528 | void checkarg (char *arg) 529 | { 530 | long i; 531 | char c; 532 | 533 | if (*arg == '#') 534 | { 535 | if (sscanf (arg, "#%ld%c", & i, & c) != 1) 536 | fatal (1, "bad argument: '%s'\n", arg); 537 | } 538 | } 539 | 540 | 541 | int main (int argc, char *argv[]) 542 | { 543 | long i; 544 | char action = '\0'; 545 | char* inputname = NULL; 546 | char *arg; 547 | 548 | progname = argv [0]; 549 | argcount = 0; 550 | 551 | while (--argc > 0) 552 | { 553 | ++argv; 554 | arg = argv [0]; 555 | if (arg [0] == '-') 556 | { 557 | while (*++arg) 558 | switch (*arg) 559 | { 560 | case '8': 561 | eightbit = true; break; 562 | case 'd': 563 | buildtree = true; break; 564 | case 'f': 565 | if (--argc < 0) 566 | fatal (1, "input file name missing\n"); 567 | inputname = (++argv) [0]; 568 | break; 569 | case 't': 570 | verbose++; 571 | /* Fall through. */ 572 | case 'x': 573 | action = *arg; break; 574 | case 'v': 575 | verbose++; break; 576 | default: 577 | fatal (1, "bad option %c\n", arg [1]); 578 | } 579 | } 580 | else 581 | { 582 | if (argcount >= MAX_FILE_SPEC) 583 | fatal (1, "too many file specs\n"); 584 | argfiles [argcount++] = argv [0]; 585 | } 586 | } 587 | 588 | if (! action) 589 | fatal (1, "either -t or -x must be specified\n"); 590 | 591 | for (i = 0; i < argcount; i++) 592 | checkarg (argfiles[i]); 593 | 594 | if (inputname == NULL) 595 | fatal (1, "no input file given\n"); 596 | 597 | tape = opentape (inputname, 0, 0); 598 | if (! tape) 599 | fatal (1, "can't open %s for input\n", inputname); 600 | 601 | switch (action) { 602 | case 't': dodirectory (); break; 603 | case 'x': doextract (); break; 604 | } 605 | 606 | return 0; 607 | } 608 | --------------------------------------------------------------------------------