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