New upstream version 2.20
[debian/cpmtools] / cpmcp.c
1 /* #includes */ /*{{{C}}}*//*{{{*/
2 #include "config.h"
3
4 #include <sys/stat.h>
5 #include <sys/types.h>
6 #include <ctype.h>
7 #include <errno.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <limits.h>
11 #include <stdlib.h>
12 #include <utime.h>
13
14 #include "getopt_.h"
15 #include "cpmfs.h"
16
17 #ifdef USE_DMALLOC
18 #include <dmalloc.h>
19 #endif
20 /*}}}*/
21
22 const char cmd[]="cpmcp";
23 static int text=0;
24 static int preserve=0;
25
26 /**
27  * Return the user number.
28  * @param s CP/M filename in 0[0]:aaaaaaaa.bbb format.
29  * @returns The user number or -1 for no match.
30  */
31 static int userNumber(const char *s) /*{{{*/
32 {
33   if (isdigit(*s) && *(s+1)==':') return (*s-'0');
34   if (isdigit(*s) && isdigit(*(s+1)) && *(s+2)==':') return (10*(*s-'0')+(*(s+1)-'0'));
35   return -1;
36 }
37 /*}}}*/
38
39 /**
40  * Copy one file from CP/M to UNIX.
41  * @param root The inode for the root directory.
42  * @param src  The CP/M filename in 00aaaaaaaabbb format.
43  * @param dest The UNIX filename.
44  * @returns 0 for success, 1 for error.
45  */
46 static int cpmToUnix(const struct cpmInode *root, const char *src, const char *dest) /*{{{*/
47 {
48   struct cpmInode ino;
49   int exitcode=0;
50
51   if (cpmNamei(root,src,&ino)==-1) { fprintf(stderr,"%s: can not open `%s': %s\n",cmd,src,boo); exitcode=1; }
52   else
53   {
54     struct cpmFile file;
55     FILE *ufp;
56
57     cpmOpen(&ino,&file,O_RDONLY);
58     if ((ufp=fopen(dest,text ? "w" : "wb"))==(FILE*)0) { fprintf(stderr,"%s: can not create %s: %s\n",cmd,dest,strerror(errno)); exitcode=1; }
59     else
60     {
61       int crpending=0;
62       int ohno=0;
63       int res;
64       char buf[4096];
65
66       while ((res=cpmRead(&file,buf,sizeof(buf)))>0)
67       {
68         int j;
69
70         for (j=0; j<res; ++j)
71         {
72           if (text)
73           {
74             if (buf[j]=='\032') goto endwhile;
75             if (crpending)
76             {
77               if (buf[j]=='\n') 
78               {
79                 if (putc('\n',ufp)==EOF) { fprintf(stderr,"%s: can not write %s: %s\n",cmd,dest,strerror(errno)); exitcode=1; ohno=1; goto endwhile; }
80                 crpending=0;
81               }
82               else if (putc('\r',ufp)==EOF) { fprintf(stderr,"%s: can not write %s: %s\n",cmd,dest,strerror(errno)); exitcode=1; ohno=1; goto endwhile; }
83               crpending=(buf[j]=='\r');
84             }
85             else
86             {
87               if (buf[j]=='\r') crpending=1;
88               else if (putc(buf[j],ufp)==EOF) { fprintf(stderr,"%s: can not write %s: %s\n",cmd,dest,strerror(errno)); exitcode=1; ohno=1; goto endwhile; }
89             }
90           }
91           else if (putc(buf[j],ufp)==EOF) { fprintf(stderr,"%s: can not write %s: %s\n",cmd,dest,strerror(errno)); exitcode=1; ohno=1; goto endwhile; }
92         }
93       }
94       endwhile:
95       if (res==-1 && !ohno) { fprintf(stderr,"%s: can not read %s (%s)\n",cmd,src,boo); exitcode=1; ohno=1; }
96       if (fclose(ufp)==EOF && !ohno) { fprintf(stderr,"%s: can not close %s: %s\n",cmd,dest,strerror(errno)); exitcode=1; ohno=1; }
97       if (preserve && !ohno && (ino.atime || ino.mtime))
98       {
99         struct utimbuf ut;
100
101         if (ino.atime) ut.actime=ino.atime; else time(&ut.actime);
102         if (ino.mtime) ut.modtime=ino.mtime; else time(&ut.modtime);
103         if (utime(dest,&ut)==-1) { fprintf(stderr,"%s: can change timestamps of %s: %s\n",cmd,dest,strerror(errno)); exitcode=1; ohno=1; }
104       }
105     }
106     cpmClose(&file);
107   }
108   return exitcode;
109 }
110 /*}}}*/
111
112 static void usage(void) /*{{{*/
113 {
114   fprintf(stderr,"Usage: %s [-f format] [-p] [-t] image user:file file\n",cmd);
115   fprintf(stderr,"       %s [-f format] [-p] [-t] image user:file ... directory\n",cmd);
116   fprintf(stderr,"       %s [-f format] [-p] [-t] image file user:file\n",cmd);
117   fprintf(stderr,"       %s [-f format] [-p] [-t] image file ... user:\n",cmd);
118   exit(1);
119 }
120 /*}}}*/
121
122 int main(int argc, char *argv[])
123 {
124   /* variables */ /*{{{*/
125   const char *err;
126   const char *image;
127   const char *format;
128   const char *devopts=NULL;
129   int c,readcpm=-1,todir=-1;
130   struct cpmInode root;
131   struct cpmSuperBlock super;
132   int exitcode=0;
133   int gargc;
134   char **gargv;
135   /*}}}*/
136
137   /* parse options */ /*{{{*/
138   if (!(format=getenv("CPMTOOLSFMT"))) format=FORMAT;
139   while ((c=getopt(argc,argv,"T:f:h?pt"))!=EOF) switch(c)
140   {
141     case 'T': devopts=optarg; break;
142     case 'f': format=optarg; break;
143     case 'h':
144     case '?': usage(); break;
145     case 'p': preserve=1; break;
146     case 't': text=1; break;
147   }
148   /*}}}*/
149   /* parse arguments */ /*{{{*/
150   if ((optind+2)>=argc) usage();
151   image=argv[optind++];
152
153   if (userNumber(argv[optind])>=0) /* cpm -> unix? */ /*{{{*/
154   {
155     int i;
156     struct stat statbuf;
157
158     for (i=optind; i<(argc-1); ++i) if (userNumber(argv[i])==-1) usage();
159     todir=((argc-optind)>2);
160     if (stat(argv[argc-1],&statbuf)==-1) { if (todir) usage(); }
161     else if (S_ISDIR(statbuf.st_mode)) todir=1; else if (todir) usage();
162     readcpm=1;
163   }
164   /*}}}*/
165   else if (userNumber(argv[argc-1])>=0) /* unix -> cpm */ /*{{{*/
166   {
167     int i;
168
169     todir=0;
170     for (i=optind; i<(argc-1); ++i) if (userNumber(argv[i])>=0) usage();
171     if ((argc-optind)>2 && *(strchr(argv[argc-1],':')+1)!='\0') usage();
172     if (*(strchr(argv[argc-1],':')+1)=='\0') todir=1;
173     readcpm=0;
174   }
175   /*}}}*/
176   else usage();
177   /*}}}*/
178   /* open image file */ /*{{{*/
179   if ((err=Device_open(&super.dev,image,readcpm ? O_RDONLY : O_RDWR, devopts)))
180   {
181     fprintf(stderr,"%s: cannot open %s (%s)\n",cmd,image,err);
182     exit(1);
183   }
184   if (cpmReadSuper(&super,&root,format)==-1)
185   {
186     fprintf(stderr,"%s: cannot read superblock (%s)\n",cmd,boo);
187     exit(1);
188   }
189   /*}}}*/
190   if (readcpm) /* copy from CP/M to UNIX */ /*{{{*/
191   {
192     int i;
193     char *last=argv[argc-1];
194     
195     cpmglob(optind,argc-1,argv,&root,&gargc,&gargv);
196     /* trying to copy multiple files to a file? */
197     if (gargc>1 && !todir) usage();
198     for (i=0; i<gargc; ++i)
199     {
200       char dest[_POSIX_PATH_MAX];
201
202       if (todir)
203       {
204         strcpy(dest,last);
205         strcat(dest,"/");
206         strcat(dest,gargv[i]+2);
207       }
208       else strcpy(dest,last);
209       if (cpmToUnix(&root,gargv[i],dest)) exitcode=1;
210     }
211   }
212   /*}}}*/
213   else /* copy from UNIX to CP/M */ /*{{{*/
214   {
215     int i;
216
217     for (i=optind; i<(argc-1); ++i)
218     {
219       /* variables */ /*{{{*/
220       char *dest=(char*)0;
221       FILE *ufp;
222       /*}}}*/
223
224       if ((ufp=fopen(argv[i],"rb"))==(FILE*)0) /* cry a little */ /*{{{*/
225       {
226         fprintf(stderr,"%s: can not open %s: %s\n",cmd,argv[i],strerror(errno));
227         exitcode=1;
228       }
229       /*}}}*/
230       else
231       {
232         struct cpmInode ino;
233         char cpmname[2+8+1+3+1]; /* 00foobarxy.zzy\0 */
234         struct stat st;
235
236         stat(argv[i],&st);
237
238         if (todir)
239         {
240           if ((dest=strrchr(argv[i],'/'))!=(char*)0) ++dest; else dest=argv[i];
241           snprintf(cpmname,sizeof(cpmname),"%02d%s",userNumber(argv[argc-1]),dest);
242         }
243         else
244         {
245           snprintf(cpmname,sizeof(cpmname),"%02d%s",userNumber(argv[argc-1]),strchr(argv[argc-1],':')+1);
246         }
247         if (cpmCreat(&root,cpmname,&ino,0666)==-1) /* just cry */ /*{{{*/
248         {
249           fprintf(stderr,"%s: can not create %s: %s\n",cmd,cpmname,boo);
250           exitcode=1;
251         }
252         /*}}}*/
253         else
254         {
255           struct cpmFile file;
256           int ohno=0;
257           char buf[4096+1];
258
259           cpmOpen(&ino,&file,O_WRONLY);
260           do
261           {
262             unsigned int j;
263
264             for (j=0; j<(sizeof(buf)/2) && (c=getc(ufp))!=EOF; ++j)
265             {
266               if (text && c=='\n') buf[j++]='\r';
267               buf[j]=c;
268             }
269             if (text && c==EOF) buf[j++]='\032';
270             if (cpmWrite(&file,buf,j)!=(ssize_t)j)
271             {
272               fprintf(stderr,"%s: can not write %s: %s\n",cmd,dest,boo);
273               ohno=1;
274               exitcode=1;
275               break;
276             }
277           } while (c!=EOF);
278           if (cpmClose(&file)==EOF && !ohno) /* I just can't hold back the tears */ /*{{{*/
279           {
280             fprintf(stderr,"%s: can not close %s: %s\n",cmd,dest,boo);
281             exitcode=1;
282           }
283           /*}}}*/
284           if (preserve && !ohno)
285           {
286             struct utimbuf times;
287             times.actime=st.st_atime;
288             times.modtime=st.st_mtime;
289             cpmUtime(&ino,&times);
290           }
291         }
292         fclose(ufp);
293       }
294     }
295   }
296   /*}}}*/
297   cpmUmount(&super);
298   exit(exitcode);
299 }