Add --help and --version options.
[platform/upstream/coreutils.git] / src / install.c
1 /* install - copy files and set attributes
2    Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
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 as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
17
18 /* Copy files and set their permission modes and, if possible,
19    their owner and group.  Used similarly to `cp'; typically
20    used in Makefiles to copy programs into their destination
21    directories.  It can also be used to create the destination
22    directories and any leading directories, and to set the final
23    directory's modes.  It refuses to copy files onto themselves.
24
25    Options:
26    -g, --group=GROUP
27         Set the group ownership of the installed file or directory
28         to the group ID of GROUP (default is process's current
29         group).  GROUP may also be a numeric group ID.
30
31    -m, --mode=MODE
32         Set the permission mode for the installed file or directory
33         to MODE, which is an octal number (default is 0755).
34
35    -o, --owner=OWNER
36         If run as root, set the ownership of the installed file to
37         the user ID of OWNER (default is root).  OWNER may also be
38         a numeric user ID.
39
40    -c   No effect.  For compatibility with old Unix versions of install.
41
42    -s, --strip
43         Strip the symbol tables from installed files.
44
45    -d, --directory
46         Create a directory and its leading directories, if they
47         do not already exist.  Set the owner, group and mode
48         as given on the command line.  Any leading directories
49         that are created are also given those attributes.
50         This is different from the SunOS 4.0 install, which gives
51         directories that it creates the default attributes.
52
53    David MacKenzie <djm@gnu.ai.mit.edu> */
54
55 #include <stdio.h>
56 #include <getopt.h>
57 #include <ctype.h>
58 #include <sys/types.h>
59 #include <pwd.h>
60 #include <grp.h>
61 #include "system.h"
62 #include "version.h"
63 #include "modechange.h"
64
65 #if !defined (isascii) || defined (STDC_HEADERS)
66 #undef isascii
67 #define isascii(c) 1
68 #endif
69
70 #define ISDIGIT(c) (isascii (c) && isdigit (c))
71
72 #ifdef _POSIX_VERSION
73 #include <sys/wait.h>
74 #else
75 struct passwd *getpwnam ();
76 struct group *getgrnam ();
77 uid_t getuid ();
78 gid_t getgid ();
79 int wait ();
80 #endif
81
82 #ifdef _POSIX_SOURCE
83 #define endgrent()
84 #define endpwent()
85 #endif
86
87 /* True if C is an ASCII octal digit. */
88 #define isodigit(c) ((c) >= '0' && c <= '7')
89
90 /* Number of bytes of a file to copy at a time. */
91 #define READ_SIZE (32 * 1024)
92
93 char *basename ();
94 char *xmalloc ();
95 void error ();
96 int make_path ();
97 int isdir ();
98
99 static int change_attributes ();
100 static int copy_file ();
101 static int install_file_in_dir ();
102 static int install_file_in_file ();
103 static int isnumber ();
104 static void get_ids ();
105 static void strip ();
106 static void usage ();
107
108 /* The name this program was run with, for error messages. */
109 char *program_name;
110
111 /* The user name that will own the files, or NULL to make the owner
112    the current user ID. */
113 static char *owner_name;
114
115 /* The user ID corresponding to `owner_name'. */
116 static uid_t owner_id;
117
118 /* The group name that will own the files, or NULL to make the group
119    the current group ID. */
120 static char *group_name;
121
122 /* The group ID corresponding to `group_name'. */
123 static gid_t group_id;
124
125 /* The permissions to which the files will be set.  The umask has
126    no effect. */
127 static int mode;
128
129 /* If nonzero, strip executable files after copying them. */
130 static int strip_files;
131
132 /* If nonzero, install a directory instead of a regular file. */
133 static int dir_arg;
134
135 /* If non-zero, display usage information and exit.  */
136 static int flag_help;
137
138 /* If non-zero, print the version on standard error.  */
139 static int flag_version;
140
141 static struct option const long_options[] =
142 {
143   {"strip", no_argument, NULL, 's'},
144   {"directory", no_argument, NULL, 'd'},
145   {"group", required_argument, NULL, 'g'},
146   {"mode", required_argument, NULL, 'm'},
147   {"owner", required_argument, NULL, 'o'},
148   {"help", no_argument, &flag_help, 1},
149   {"version", no_argument, &flag_version, 1},
150   {NULL, 0, NULL, 0}
151 };
152
153 void
154 main (argc, argv)
155      int argc;
156      char **argv;
157 {
158   int optc;
159   int errors = 0;
160   char *symbolic_mode = NULL;
161
162   program_name = argv[0];
163   owner_name = NULL;
164   group_name = NULL;
165   mode = 0755;
166   strip_files = 0;
167   dir_arg = 0;
168   umask (0);
169
170   while ((optc = getopt_long (argc, argv, "csdg:m:o:", long_options,
171                               (int *) 0)) != EOF)
172     {
173       switch (optc)
174         {
175         case 'c':
176           break;
177         case 's':
178           strip_files = 1;
179           break;
180         case 'd':
181           dir_arg = 1;
182           break;
183         case 'g':
184           group_name = optarg;
185           break;
186         case 'm':
187           symbolic_mode = optarg;
188           break;
189         case 'o':
190           owner_name = optarg;
191           break;
192         default:
193           usage ();
194         }
195     }
196
197   /* Check for invalid combinations of arguments. */
198   if ((dir_arg && strip_files)
199       || (optind == argc)
200       || (optind == argc - 1 && !dir_arg))
201     usage ();
202
203   if (flag_version)
204     fprintf (stderr, "%s\n", version_string);
205
206   if (flag_help)
207     usage ();
208
209   if (symbolic_mode)
210     {
211       struct mode_change *change = mode_compile (symbolic_mode, 0);
212       if (change == MODE_INVALID)
213         error (1, 0, "invalid mode `%s'", symbolic_mode);
214       else if (change == MODE_MEMORY_EXHAUSTED)
215         error (1, 0, "virtual memory exhausted");
216       mode = mode_adjust (0, change);
217     }
218
219   get_ids ();
220
221   if (dir_arg)
222     {
223       for (; optind < argc; ++optind)
224         {
225           errors |=
226             make_path (argv[optind], mode, mode, owner_id, group_id, NULL);
227         }
228     }
229   else
230     {
231       if (optind == argc - 2)
232         {
233           if (!isdir (argv[argc - 1]))
234             errors = install_file_in_file (argv[argc - 2], argv[argc - 1]);
235           else
236             errors = install_file_in_dir (argv[argc - 2], argv[argc - 1]);
237         }
238       else
239         {
240           if (!isdir (argv[argc - 1]))
241             usage ();
242           for (; optind < argc - 1; ++optind)
243             {
244               errors |= install_file_in_dir (argv[optind], argv[argc - 1]);
245             }
246         }
247     }
248
249   exit (errors);
250 }
251
252 /* Copy file FROM onto file TO and give TO the appropriate
253    attributes.
254    Return 0 if successful, 1 if an error occurs. */
255
256 static int
257 install_file_in_file (from, to)
258      char *from;
259      char *to;
260 {
261   if (copy_file (from, to))
262     return 1;
263   if (strip_files)
264     strip (to);
265   return change_attributes (to);
266 }
267
268 /* Copy file FROM into directory TO_DIR, keeping its same name,
269    and give the copy the appropriate attributes.
270    Return 0 if successful, 1 if not. */
271
272 static int
273 install_file_in_dir (from, to_dir)
274      char *from;
275      char *to_dir;
276 {
277   char *from_base;
278   char *to;
279   int ret;
280
281   from_base = basename (from);
282   to = xmalloc ((unsigned) (strlen (to_dir) + strlen (from_base) + 2));
283   sprintf (to, "%s/%s", to_dir, from_base);
284   ret = install_file_in_file (from, to);
285   free (to);
286   return ret;
287 }
288
289 /* A chunk of a file being copied. */
290 static char buffer[READ_SIZE];
291
292 /* Copy file FROM onto file TO, creating TO if necessary.
293    Return 0 if the copy is successful, 1 if not. */
294
295 static int
296 copy_file (from, to)
297      char *from;
298      char *to;
299 {
300   int fromfd, tofd;
301   int bytes;
302   int ret = 0;
303   struct stat from_stats, to_stats;
304
305   if (stat (from, &from_stats))
306     {
307       error (0, errno, "%s", from);
308       return 1;
309     }
310   if (!S_ISREG (from_stats.st_mode))
311     {
312       error (0, 0, "`%s' is not a regular file", from);
313       return 1;
314     }
315   if (stat (to, &to_stats) == 0)
316     {
317       if (!S_ISREG (to_stats.st_mode))
318         {
319           error (0, 0, "`%s' is not a regular file", to);
320           return 1;
321         }
322       if (from_stats.st_dev == to_stats.st_dev
323           && from_stats.st_ino == to_stats.st_ino)
324         {
325           error (0, 0, "`%s' and `%s' are the same file", from, to);
326           return 1;
327         }
328       /* If unlink fails, try to proceed anyway.  */
329       unlink (to);
330     }
331
332   fromfd = open (from, O_RDONLY, 0);
333   if (fromfd == -1)
334     {
335       error (0, errno, "%s", from);
336       return 1;
337     }
338
339   /* Make sure to open the file in a mode that allows writing. */
340   tofd = open (to, O_WRONLY | O_CREAT | O_TRUNC, 0600);
341   if (tofd == -1)
342     {
343       error (0, errno, "%s", to);
344       close (fromfd);
345       return 1;
346     }
347
348   while ((bytes = read (fromfd, buffer, READ_SIZE)) > 0)
349     if (write (tofd, buffer, bytes) != bytes)
350       {
351         error (0, errno, "%s", to);
352         goto copy_error;
353       }
354
355   if (bytes == -1)
356     {
357       error (0, errno, "%s", from);
358       goto copy_error;
359     }
360
361   if (close (fromfd) < 0)
362     {
363       error (0, errno, "%s", from);
364       ret = 1;
365     }
366   if (close (tofd) < 0)
367     {
368       error (0, errno, "%s", to);
369       ret = 1;
370     }
371   return ret;
372
373  copy_error:
374   close (fromfd);
375   close (tofd);
376   return 1;
377 }
378
379 /* Set the attributes of file or directory PATH.
380    Return 0 if successful, 1 if not. */
381
382 static int
383 change_attributes (path)
384      char *path;
385 {
386   int err = 0;
387
388   /* chown must precede chmod because on some systems,
389      chown clears the set[ug]id bits for non-superusers,
390      resulting in incorrect permissions.
391      On System V, users can give away files with chown and then not
392      be able to chmod them.  So don't give files away.
393
394      We don't pass -1 to chown to mean "don't change the value"
395      because SVR3 and earlier non-BSD systems don't support that.
396
397      We don't normally ignore errors from chown because the idea of
398      the install command is that the file is supposed to end up with
399      precisely the attributes that the user specified (or defaulted).
400      If the file doesn't end up with the group they asked for, they'll
401      want to know.  But AFS returns EPERM when you try to change a
402      file's group; thus the kludge.  */
403
404   if (chown (path, owner_id, group_id)
405 #ifdef AFS
406       && errno != EPERM
407 #endif
408       )
409     err = errno;
410   if (chmod (path, mode))
411     err = errno;
412   if (err)
413     {
414       error (0, err, "%s", path);
415       return 1;
416     }
417   return 0;
418 }
419
420 /* Strip the symbol table from the file PATH.
421    We could dig the magic number out of the file first to
422    determine whether to strip it, but the header files and
423    magic numbers vary so much from system to system that making
424    it portable would be very difficult.  Not worth the effort. */
425
426 static void
427 strip (path)
428      char *path;
429 {
430   int pid, status;
431
432   pid = fork ();
433   switch (pid)
434     {
435     case -1:
436       error (1, errno, "cannot fork");
437       break;
438     case 0:                     /* Child. */
439       execlp ("strip", "strip", path, (char *) NULL);
440       error (1, errno, "cannot run strip");
441       break;
442     default:                    /* Parent. */
443       /* Parent process. */
444       while (pid != wait (&status))     /* Wait for kid to finish. */
445         /* Do nothing. */ ;
446       break;
447     }
448 }
449
450 /* Initialize the user and group ownership of the files to install. */
451
452 static void
453 get_ids ()
454 {
455   struct passwd *pw;
456   struct group *gr;
457
458   if (owner_name)
459     {
460       pw = getpwnam (owner_name);
461       if (pw == NULL)
462         {
463           if (!isnumber (owner_name))
464             error (1, 0, "invalid user `%s'", owner_name);
465           owner_id = atoi (owner_name);
466         }
467       else
468         owner_id = pw->pw_uid;
469       endpwent ();
470     }
471   else
472     owner_id = getuid ();
473
474   if (group_name)
475     {
476       gr = getgrnam (group_name);
477       if (gr == NULL)
478         {
479           if (!isnumber (group_name))
480             error (1, 0, "invalid group `%s'", group_name);
481           group_id = atoi (group_name);
482         }
483       else
484         group_id = gr->gr_gid;
485       endgrent ();
486     }
487   else
488     group_id = getgid ();
489 }
490
491 /* Return nonzero if STR is an ASCII representation of a nonzero
492    decimal integer, zero if not. */
493
494 static int
495 isnumber (str)
496      char *str;
497 {
498   if (*str == 0)
499     return 0;
500   for (; *str; str++)
501     if (!ISDIGIT (*str))
502       return 0;
503   return 1;
504 }
505
506 static void
507 usage ()
508 {
509    fprintf (stderr, "\
510 Usage: %s [options] [-s] [--strip] source dest\n\
511        %s [options] [-s] [--strip] source... directory\n\
512        %s [options] {-d,--directory} directory...\n\
513 Options:\n\
514        [-c] [-g group] [-m mode] [-o owner]\n\
515        [--group=group] [--mode=mode] [--owner=owner]\n",
516             program_name, program_name, program_name);
517   exit (1);
518 }