home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / c_spec / sources / cp.c < prev    next >
C/C++ Source or Header  |  1986-02-20  |  8KB  |  354 lines

  1. #include <stdio.h>
  2. #include <fcntl.h>
  3. #include <types.h>
  4. #include <stat.h>
  5. #include <errno.h>
  6.  
  7. /*
  8.  *    CP.C:  Copyright (C) 1985, Allen I. Holub. All rights reserved.
  9.  *
  10.  *    A version of the unix copy utility for MSDOS. Note
  11.  *    That unlike copy you have to specify a target.
  12.  *    So, a file to the current directory you have to say:
  13.  *
  14.  *                cp <src file> .
  15.  *
  16.  *    (the '.' is the current directory).
  17.  *
  18.  *    Usage:        cp [-ie] file [... file] target
  19.  *                    where "target" may be a filename of
  20.  *                    a directory name. In the first case
  21.  *
  22.  *    -i    Interactive, ask before copying anything.
  23.  *    -v    Verfify before overwriting an existing file
  24.  *    -e    Do not echo actions to screen as they are done (this will
  25.  *        make cp behave like the unix version, which is silent).
  26.  *
  27.  *    Exit status: 0 if copy is made successfully, 1 otherwise;
  28.  */
  29.  
  30. extern int    errno;
  31. extern char    *malloc    (unsigned);
  32. extern int    open    (char*,int,);
  33. extern int    read    (int, char*, unsigned);
  34. extern int    stat    (char*,struct stat *);
  35. extern int    write    (int, char*, int);
  36. extern char    *cpy    (char*, char*);
  37.  
  38. #define isslash(c) ((c)=='/' || (c) == '\\')
  39.  
  40. /*---------------------------------------------------------------------*/
  41.  
  42. int    Verify = 0;    /* Verify before overwriting existing file     */
  43. int    Echo   = 1;    /* Say what we're doing as we do it           */
  44. int    Interactive = 0;/* Ask before copying anyting               */
  45.  
  46. /*---------------------------------------------------------------------*/
  47.  
  48. args(argc, argv)
  49. char    **argv;
  50. {
  51.     /*    Process command line arguments. If -i iv found set
  52.      *    Verify true, if -e is found set Echo false. Return
  53.      *    1 if we found an argument, 0 otherwise
  54.      *    On entry argv should point at argv[1]
  55.      */
  56.  
  57.     register char    *p;
  58.  
  59.  
  60.     if( **argv == '-' )
  61.     {
  62.         for( p = *argv + 1 ; *p ; p++ )
  63.         {
  64.             if      ( *p == 'v' )    Verify         = 1;
  65.             else if ( *p == 'i' )    Interactive = 1;
  66.             else if ( *p == 'e' )    Echo        = 0;
  67.             else    usage("Illegal argument" );
  68.         }
  69.  
  70.         if( Verify || Interactive )
  71.             Echo = 1;
  72.  
  73.         return 1;
  74.     }
  75.  
  76.     return 0;
  77. }
  78.  
  79. /*----------------------------------------------------------------------*/
  80.  
  81. doargs( argcp, argvp )
  82. char    ***argvp;
  83. int    *argcp;
  84. {
  85.     /*    Parse the command line arguements and modify argv and
  86.      *    argc so that the filename and arguments (if present)
  87.      *    will be removed.
  88.      */
  89.  
  90.     char    **argv  = *argvp;
  91.     int    argc    = *argcp;
  92.  
  93.     if( argc < 3 )
  94.         usage("Too few arguments");
  95.  
  96.     if( args(--argc, ++argv) )    /* Process command line arguments */
  97.     {                /* and delete file name and any   */
  98.         ++argv ;        /* arguments from argv          */
  99.         --argc ; 
  100.     }
  101.  
  102.     *argvp = argv;
  103.     *argcp = argc;
  104. }
  105.  
  106.  
  107. /*----------------------------------------------------------------------*/
  108.  
  109. int    getbuf( bufp )
  110. char    **bufp;
  111. {
  112.     register unsigned bsize = 0x7fff;   /* Buffer size        */
  113.     register char       *buf;
  114.  
  115.     /*    Get the biggest buffer you can from malloc.
  116.     */
  117.  
  118.     while( !( buf = malloc(bsize)) &&  (int)bsize > 0  )
  119.         bsize -= 512 ;
  120.  
  121.     *bufp = buf;
  122.     return bsize;
  123. }
  124.  
  125. /*----------------------------------------------------------------------*/
  126.  
  127. getdisk( pp )
  128. char    **pp;
  129. {
  130.     register char    *p = *pp;
  131.  
  132.     if( p[0] && p[1] == ':' )
  133.     {
  134.         *pp = p + 2;
  135.         return *p;
  136.     }
  137.     else
  138.         return 0;
  139. }
  140.  
  141. /*----------------------------------------------------------------------*/
  142.  
  143. char    *fname( str )
  144. char    *str;
  145. {
  146.     /* Return a pointer to the filename part of str.
  147.      */
  148.  
  149.     register char    *last, *s ;
  150.     
  151.     for( s = last = str;  *s ; s++ )
  152.         if( isslash(*s) || *s == ':' )
  153.             last = s;
  154.  
  155.     return( last == str ?  str : last + 1 );
  156. }
  157.  
  158. /*---------------------------------------------------------------------*/
  159.  
  160. char *make_tname( disk, targ, isfile, srcname )
  161. char    *targ, *srcname;
  162. {
  163.     static   char    tbuf[128] ;
  164.     register char    *p = tbuf;
  165.  
  166.     if( disk )            /* Copy the disk id if one    */
  167.     {                /* exists and a trailing :    */
  168.         *p++ = disk;
  169.         *p++ = ':';
  170.  
  171.         if( !*targ )
  172.         {
  173.             *p++ = '.';
  174.             isfile = 0;
  175.         }
  176.     }
  177.  
  178.     p = cpy( p, targ );        /* Copy the target name     */
  179.  
  180.     if( !isfile )
  181.     {
  182.         if( p != tbuf && p[-1] != '/')  /* If target is a    */
  183.             *p++ = '/' ;        /* directory copy a    */
  184.                         /* slash and the file    */
  185.         cpy( p, fname( srcname ) );    /* name part of the    */
  186.     }                    /* srcname.        */
  187.  
  188.     return tbuf;
  189. }
  190.  
  191. /*----------------------------------------------------------------------*/
  192.  
  193. yes(str)
  194. char    *str;
  195. {
  196.     register int    c;
  197.  
  198.     fprintf(stderr, "%s (y/n): ",    str );
  199.     fprintf(stderr, "%c\n",        c = getch() );
  200.  
  201.     return( c == 'y' || c == 'Y' );
  202. }
  203.  
  204. /*----------------------------------------------------------------------*/
  205.  
  206. exists( file )
  207. char    *file;
  208. {
  209.     /*    Return 1 if the file exists, 0 if not
  210.     */
  211.  
  212.     struct   stat    status;
  213.  
  214.     return( stat(file, &status) == 0 );
  215. }
  216.  
  217. /*----------------------------------------------------------------------*/
  218.  
  219. go_ahead( src, dest )
  220. char    *src, *dest;
  221. {
  222.     if( Echo )
  223.         fprintf(stderr, "copy: %20s to %-20s ", src, dest );
  224.  
  225.     if( Interactive )
  226.         return( yes("?") );
  227.  
  228.     else if( Verify && exists( dest ) )
  229.         return( yes("overwrite?") );
  230.  
  231.     putc('\n', stderr );
  232. }
  233.  
  234. /*----------------------------------------------------------------------*/
  235.  
  236. doerror( fname,  read_write )
  237. char    *fname, *read_write;
  238. {
  239.     fprintf(stderr,"\nCan't open %s for %s: ", fname, read_write );
  240.     
  241.     fprintf(stderr,( errno == ENOENT ) 
  242.             ? "File not found\n"
  243.             : "File is a directory or is write protected\n" );
  244. }
  245.  
  246. /*----------------------------------------------------------------------*/
  247.  
  248. main(argc, argv)
  249. char    **argv;
  250. {
  251.     char        *buf;
  252.     char        *targ;           /* Target name from cmd line    */
  253.     char        *tname;           /* Actual target name    */
  254.     int        isfile;           /* True if targ is a file     */
  255.     int        disk;           /* Disk id of target        */
  256.     register int    got;           /* # bytes got from read    */
  257.     register int    src, dest;       /* File handles        */
  258.     unsigned     bsize;           /* Buffer size        */
  259.     int        exit_status = 0 ;  /* value returned to shell    */
  260.     int        err;
  261.  
  262.  
  263.     ctlc();                /* Fix ^C Interrupt handling    */
  264.     reargv( &argc, &argv );        /* Remake argv from CMDLINE    */
  265.     doargs( &argc, &argv );        /* Parse command line args    */
  266.     bsize = getbuf( &buf );        /* Get a buffer to use for xfer */
  267.  
  268.     targ   = argv[--argc] ;        /* targ = name of target    */
  269.     isfile = !isdir(targ) ;        /* isfile = 1 if its a file    */
  270.     disk   = getdisk( &targ );    /* extract disk id part of name */
  271.  
  272.     if( isfile && argc > 2 )
  273.         usage("%s is not a directory", targ );
  274.  
  275.     for( ; --argc >= 0 ; ++argv )
  276.     {
  277.         if( isdir( *argv ) )
  278.             continue;
  279.  
  280.         tname = make_tname( disk, targ, isfile, *argv );
  281.  
  282.         if( !go_ahead(*argv, tname) )
  283.         {
  284.             exit_status = 1;
  285.             continue;
  286.         }
  287.  
  288.         if( (src = open(*argv, O_RDONLY | O_BINARY)) == -1)
  289.         {
  290.             doerror( *argv, "read" );
  291.             exit_status = 1;
  292.             continue;
  293.         }
  294.  
  295.         dest = open(tname, O_TRUNC | O_CREAT | O_WRONLY | O_BINARY,
  296.                         S_IWRITE | S_IREAD );
  297.         if( dest == -1 )
  298.         {
  299.             exit_status = 1;
  300.             doerror( tname, "write" );
  301.             continue;
  302.         }
  303.  
  304.         while( got = read(src, buf, bsize) )
  305.         {
  306.             if( got == -1 || (err = write(dest,buf,got)) != got )
  307.             {
  308.                 fprintf( stderr,
  309.                     "No space or File marked read only\n");
  310.  
  311.                 exit_status = 1;
  312.  
  313.                 if( err == ENOSPC )
  314.                     goto abort;
  315.  
  316.                 break;
  317.             }
  318.         }
  319.         close( src  );
  320.         close( dest );
  321.     }
  322. abort:
  323.     free( buf );
  324.     exit( exit_status );
  325. }
  326.  
  327. /*---------------------------------------------------------------------*/
  328.  
  329.  
  330. #define    E(x)    fprintf(stderr,"%s\n", x);
  331.  
  332. usage(str, s)
  333. char    *str, *s;
  334. {
  335.     fprintf(stderr, str, s );
  336.     E("\n");
  337.     E("CP:   Copyright (c) 1986, Allen I. Holub. All rights reserved");
  338.     E("");
  339.     E("Usage: cp [-iev] file [... file] [d:]directory");
  340.     E("       cp [-iev] <source file>  <dest file>\n");
  341.     E("-i     ask before copying every file");
  342.     E("-v     ask only before over-writing existing files");
  343.     E("-e     don't echo names as they're copied\n");
  344.     E("Copies one file to another (the bottom syntax above) or a group");
  345.     E("of files to another directory or disk (the top syntax).");
  346.     E("Unlike the DOS \"copy\" command, a target must be named.");
  347.     E(".   (the current directory) and   ..   (the parent directory)");
  348.     E("alone or in combination (ie. \"cp file ../..\"  or");
  349.     E("\"cp d:/dir/file .\") are acceptable. If the target directory");
  350.     E("is a disk id only (ie: \"cp file b:\") the current directory");
  351.     E("on the specified disk is assumed.");
  352.     exit(1);
  353. }
  354.