1 /***************************************************************************
2  *                                                                         *
3  * This is a pre-configured single file version of chrpath for linux.      *
4  * It is used in the binary installer for OpenSCAD on Linux.               *
5  *                                                                         *
6  * This file has been created by running                                   *
7  *   cat *.h *.c | sed 's, *# *include *",//&,' > ../chrpath_linux.c       *
8  * in the configured chrpath-0.13 source directory (as found in the debian *
9  * package repository as the original download site seams to be down).     *
10  *                                                                         *
11  * chrpath is licensed under the terms of GPLv2:                           *
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 as published by   *
15  *  the Free Software Foundation; either version 2 of the License, or      *
16  *  (at your option) any later version.                                    *
17  *                                                                         *
18  *  This program is distributed in the hope that it will be useful,        *
19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
21  *  GNU General Public License for more details.                           *
22  *                                                                         *
23  * The Authors are:                                                        *
24  *                                                                         *
25  * Geoffrey Keating <geoffk@ozemail.com.au>                                *
26  *   Implemented first version of chrpath.c based on first version of      *
27  *   killrpath.                                                            *
28  * Peeter Joot <peeterj@ca.ibm.com>                                        *
29  *   Implemented first version of killrpath.c.                             *
30  * Petter Reinholdtsen <pere@hungry.com>                                   *
31  *   Collected both implementations and made userfriendly wrapper.         *
32  *                                                                         *
33  * With patches from:                                                      *
34  *                                                                         *
35  * Darren Salt <linux@youmustbejoking.demon.co.uk>                         *
36  * David Hull <hull@paracel.com>                                           *
37  * Bradford W. Johnson <bradford@math.umn.edu>                             *
38  * Thomas Anders <anders@hmi.de>                                           *
39  * Tollef Fog Heen <tollef@add.no>                                         *
40  *                                                                         *
41  ***************************************************************************/
42 
43 
44 /* config.h.  Generated by configure.  */
45 /* config.h.in.  Generated from configure.ac by autoheader.  */
46 
47 /* Define to 1 if you have the <elf.h> header file. */
48 #define HAVE_ELF_H 1
49 
50 /* Define to 1 if you have the <fcntl.h> header file. */
51 #define HAVE_FCNTL_H 1
52 
53 /* Define to 1 if you have the <getopt.h> header file. */
54 #define HAVE_GETOPT_H 1
55 
56 /* Define to 1 if you have the `getopt_long' function. */
57 #define HAVE_GETOPT_LONG 1
58 
59 /* Define to 1 if you have the <inttypes.h> header file. */
60 #define HAVE_INTTYPES_H 1
61 
62 /* Define to 1 if you have the <link.h> header file. */
63 #define HAVE_LINK_H 1
64 
65 /* Define to 1 if you have the <memory.h> header file. */
66 #define HAVE_MEMORY_H 1
67 
68 /* Define to 1 if you have the <stdint.h> header file. */
69 #define HAVE_STDINT_H 1
70 
71 /* Define to 1 if you have the <stdlib.h> header file. */
72 #define HAVE_STDLIB_H 1
73 
74 /* Define to 1 if you have the <strings.h> header file. */
75 #define HAVE_STRINGS_H 1
76 
77 /* Define to 1 if you have the <string.h> header file. */
78 #define HAVE_STRING_H 1
79 
80 /* Define to 1 if you have the <sys/link.h> header file. */
81 /* #undef HAVE_SYS_LINK_H */
82 
83 /* Define to 1 if you have the <sys/stat.h> header file. */
84 #define HAVE_SYS_STAT_H 1
85 
86 /* Define to 1 if you have the <sys/types.h> header file. */
87 #define HAVE_SYS_TYPES_H 1
88 
89 /* Define to 1 if you have the <unistd.h> header file. */
90 #define HAVE_UNISTD_H 1
91 
92 /* Name of package */
93 #define PACKAGE "chrpath"
94 
95 /* Define to the address where bug reports for this package should be sent. */
96 #define PACKAGE_BUGREPORT "pere@hungry.com"
97 
98 /* Define to the full name of this package. */
99 #define PACKAGE_NAME ""chrpath""
100 
101 /* Define to the full name and version of this package. */
102 #define PACKAGE_STRING ""chrpath" "0.13""
103 
104 /* Define to the one symbol short name of this package. */
105 #define PACKAGE_TARNAME "-chrpath-"
106 
107 /* Define to the version of this package. */
108 #define PACKAGE_VERSION ""0.13""
109 
110 /* Define to 1 if you have the ANSI C header files. */
111 #define STDC_HEADERS 1
112 
113 /* Version number of package */
114 #define VERSION "0.13"
115 
116 /* Define to 1 if your processor stores words with the most significant byte
117    first (like Motorola and SPARC, unlike Intel and VAX). */
118 /* #undef WORDS_BIGENDIAN */
119 
120 /* Define to empty if `const' does not conform to ANSI C. */
121 /* #undef const */
122 #ifndef PROTOS_H
123 #define PROTOS_H
124 
125 #include <elf.h>
126 //#include "config.h"
127 
128 #ifdef WORDS_BIGENDIAN
129 #define ELFDATA2 ELFDATA2MSB
130 #else
131 #define ELFDATA2 ELFDATA2LSB
132 #endif
133 #if SIZEOF_VOID_P == 8
134 #define Elf_Ehdr Elf64_Ehdr
135 #define ELFCLASS ELFCLASS64
136 #define Elf_Phdr Elf64_Phdr
137 #define Elf_Shdr Elf64_Shdr
138 #define Elf_Dyn  Elf64_Dyn
139 #elif SIZEOF_VOID_P == 4
140 #define Elf_Ehdr Elf32_Ehdr
141 #define ELFCLASS ELFCLASS32
142 #define Elf_Phdr Elf32_Phdr
143 #define Elf_Shdr Elf32_Shdr
144 #define Elf_Dyn  Elf32_Dyn
145 #else
146 #error "Unknown word size (SIZEOF_VOID_P)!"
147 #endif
148 
149 int killrpath(const char *filename);
150 int chrpath(const char *filename, const char *newpath, int convert);
151 
152 int elf_open(const char *filename, int flags, Elf_Ehdr *ehdr);
153 void elf_close(int fd);
154 int elf_find_dynamic_section(int fd, Elf_Ehdr *ehdr, Elf_Phdr *phdr);
155 const char *elf_tagname(int tag);
156 int elf_dynpath_tag(int tag);
157 
158 #endif /* PROTOS_H */
159 /*
160 <URL:http://gcc.gnu.org/ml/gcc/1999-04n/msg01105.html>
161 
162 Re: changing embedded RPATH in existing executables.
163 
164   To: geoffk@ozemail.com.au
165   Subject: Re: changing embedded RPATH in existing executables.
166   From: <peeter_joot@VNET.IBM.COM> (peeter joot)
167   Date: Fri, 30 Apr 1999 16:14:44 -0400 (EDT)
168   Cc: peeterj@ca.ibm.com, egcs@cygnus.com, libc-hacker@cygnus.com, linux-gcc@vger.rutgers.edu
169   Reply-To: <peeter_joot@VNET.IBM.COM>
170 
171 > _Changing_ is a little tricky, but the attached program strips rpaths
172 > from executables (I find it essential for debugging the binutils).
173 > It's endian-dependent, if you want this for x86 you can just change
174 > the occurrences of 'MSB' to 'LSB' and compile (I should really fix
175 > that).
176 
177 Hi Geoff,
178 
179 With your program as a guide (and some peeks into libbfd, elf.h, a bit
180 of the glibc dynamic loader code, objdump, and a hex-editor) I was able to
181 figure out enough to find and change the rpath string.  That was fun!
182 
183 This program assumes (unlike your original program) that there is only
184 one DT_RPATH tag in the dynamic section as even with multiple '-Wl,-rpath,'
185 commands in the link this seems to occur (they all get concatenated into
186 a : separated path).
187 
188 Thanks for your help.  If you want to use this on non-x86 you have to change
189 the occurrences of LSB back to MSB:)
190 
191 Peeter
192 --
193 */
194 
195 #ifdef HAVE_CONFIG_H
196 //#  include "config.h"
197 #endif
198 
199 #include <stdio.h>
200 #include <unistd.h>
201 #include <fcntl.h>
202 #include <elf.h>
203 #if defined(HAVE_LINK_H)
204 #  include <link.h>
205 #endif /* HAVE_LINK_H */
206 #include <stdlib.h>
207 #include <string.h>
208 #include <sys/stat.h>
209 //#include "protos.h"
210 
211 /**
212  * Reads an ELF file, and reads or alters the RPATH setting.
213  *
214  * TODO:
215  *  modify to add RPATH setting if none exists.
216  */
217 
218 
219 int
chrpath(const char * filename,const char * newpath,int convert)220 chrpath(const char *filename, const char *newpath, int convert)
221 {
222   int fd;
223   Elf_Ehdr ehdr;
224   int i;
225   Elf_Phdr phdr;
226   Elf_Shdr shdr;
227   Elf_Dyn *dyns;
228   int rpathoff;
229   char * strtab;
230   char * rpath;
231   unsigned int rpathlen;
232   int oflags;
233   int rpath_dyns_index;
234 
235   if (NULL == newpath && 0 == convert)
236      oflags = O_RDONLY;
237   else
238      oflags = O_RDWR;
239 
240   fd = elf_open(filename, oflags, &ehdr);
241   if (fd == -1)
242   {
243     perror ("elf_open");
244     return 1;
245   }
246 
247    if (0 != elf_find_dynamic_section(fd, &ehdr, &phdr))
248    {
249      perror("found no dynamic section");
250      return 1;
251    }
252 
253   dyns = malloc(phdr.p_filesz);
254   if (dyns == NULL)
255     {
256       perror ("allocating memory for dynamic section");
257       return 1;
258     }
259   memset(dyns, 0, phdr.p_filesz);
260   if (lseek(fd, phdr.p_offset, SEEK_SET) == -1
261       || read(fd, dyns, phdr.p_filesz) != (int)phdr.p_filesz)
262     {
263       perror ("reading dynamic section");
264       free(dyns);
265       return 1;
266     }
267 
268   rpathoff = -1;
269   for ( rpath_dyns_index = 0; dyns[rpath_dyns_index].d_tag != DT_NULL;
270         ++rpath_dyns_index )
271     {
272       if ( elf_dynpath_tag(dyns[rpath_dyns_index].d_tag) )
273       {
274          rpathoff = dyns[rpath_dyns_index].d_un.d_ptr;
275          break;
276       }
277     }
278   if (rpathoff == -1)
279     {
280       printf("%s: no rpath or runpath tag found.\n", filename);
281       free(dyns);
282       return 2;
283     }
284 
285   if (lseek(fd, ehdr.e_shoff, SEEK_SET) == -1)
286   {
287     perror ("positioning for sections");
288     free(dyns);
289     return 1;
290   }
291 
292   for (i = 0; i < ehdr.e_shnum; ++i)
293   {
294     if (read(fd, &shdr, sizeof(shdr)) != sizeof(shdr))
295     {
296       perror ("reading section header");
297       free(dyns);
298       return 1;
299     }
300     if (shdr.sh_type == SHT_STRTAB)
301       break;
302   }
303   if (i == ehdr.e_shnum)
304     {
305       fprintf (stderr, "No string table found.\n");
306       free(dyns);
307       return 2;
308     }
309   strtab = (char *)malloc(shdr.sh_size);
310   if (strtab == NULL)
311     {
312       perror ("allocating memory for string table");
313       free(dyns);
314       return 1;
315     }
316   memset(strtab, 0, shdr.sh_size);
317 
318   if (lseek(fd, shdr.sh_offset, SEEK_SET) == -1)
319   {
320     perror ("positioning for string table");
321     free(strtab);
322     free(dyns);
323     return 1;
324   }
325   if (read(fd, strtab, shdr.sh_size) != (int)shdr.sh_size)
326   {
327     perror ("reading string table");
328     free(strtab);
329     free(dyns);
330     return 1;
331   }
332 
333   if ((int)shdr.sh_size < rpathoff)
334   {
335     fprintf(stderr, "%s string offset not contained in string table",
336             elf_tagname(dyns[rpath_dyns_index].d_tag));
337     free(strtab);
338     free(dyns);
339     return 5;
340   }
341   rpath = strtab+rpathoff;
342 
343 #if defined(DT_RUNPATH)
344   if (convert && dyns[rpath_dyns_index].d_tag == DT_RPATH)
345   {
346     dyns[rpath_dyns_index].d_tag = DT_RUNPATH;
347     if (lseek(fd, phdr.p_offset, SEEK_SET) == -1
348         || write(fd, dyns, phdr.p_filesz) != (int)phdr.p_filesz)
349     {
350       perror ("converting RPATH to RUNPATH");
351       return 1;
352     }
353     printf("%s: RPATH converted to RUNPATH\n", filename);
354   }
355 #endif /* DT_RUNPATH */
356 
357   printf("%s: %s=%s\n", filename, elf_tagname(dyns[rpath_dyns_index].d_tag),
358          rpath);
359 
360   if (NULL == newpath)
361   {
362     free(dyns);
363     free(strtab);
364     return 0;
365   }
366 
367   rpathlen = strlen(rpath);
368 
369   /*
370    * Calculate the maximum rpath length (will be equal to rpathlen unless
371    * we have previously truncated it).
372    */
373   for ( i = rpathoff + rpathlen ; (i < (int)shdr.sh_size
374                                    && strtab[i] == '\0') ; i++ )
375     ;
376   i--;
377 
378   if (i > (int)(rpathoff + rpathlen))
379      rpathlen = i - rpathoff;
380 
381   if (strlen(newpath) > rpathlen)
382   {
383     fprintf(stderr, "new rpath '%s' too large; maximum length %i\n",
384             newpath, rpathlen);
385     free(dyns);
386     free(strtab);
387     return 7;
388   }
389 
390   memset(rpath, 0, rpathlen);
391   strcpy(rpath, newpath);
392 
393   if (lseek(fd, shdr.sh_offset+rpathoff, SEEK_SET) == -1)
394   {
395     perror ("positioning for RPATH");
396     free(dyns);
397     free(strtab);
398     return 1;
399   }
400   if (write(fd, rpath, rpathlen) != (int)rpathlen)
401   {
402     perror ("writing RPATH");
403     free(dyns);
404     free(strtab);
405     return 1;
406   }
407   printf("%s: new %s: %s\n", filename,
408          elf_tagname(dyns[rpath_dyns_index].d_tag), rpath);
409 
410   elf_close(fd);
411 
412   free(dyns);
413   dyns = NULL;
414 
415   free(strtab);
416 
417   return 0;
418 }
419 
420 #ifdef HAVE_CONFIG_H
421 //#  include "config.h"
422 #endif
423 
424 #include <elf.h>
425 #if defined(HAVE_SYS_LINK_H)
426 #  include <sys/link.h> /* Find DT_RPATH on Solaris 2.6 */
427 #endif /*  HAVE_SYS_LINK_H */
428 #include <stdio.h>
429 #include <string.h>
430 #include <unistd.h>
431 #include <sys/types.h>
432 #include <sys/stat.h>
433 #include <errno.h>
434 #include <fcntl.h>
435 //#include "protos.h"
436 
437 int
elf_open(const char * filename,int flags,Elf_Ehdr * ehdr)438 elf_open(const char *filename, int flags, Elf_Ehdr *ehdr)
439 {
440    int fd;
441 
442    fd = open(filename, flags);
443    if (fd == -1)
444    {
445      perror ("open");
446      return -1;
447    }
448 
449    if (read(fd, ehdr, sizeof(*ehdr)) != sizeof(*ehdr))
450    {
451      perror ("reading header");
452      close(fd);
453      return -1;
454    }
455 
456    if (0 != memcmp(ehdr->e_ident, ELFMAG, SELFMAG) ||
457        ehdr->e_ident[EI_CLASS] != ELFCLASS ||
458        ehdr->e_ident[EI_DATA] != ELFDATA2 ||
459        ehdr->e_ident[EI_VERSION] != EV_CURRENT)
460    {
461      fprintf(stderr,
462 #ifdef WORDS_BIGENDIAN
463              "`%s' probably isn't a %d-bit MSB-first ELF file.\n",
464 #else /* not WORD_BIGENDIAN */
465              "`%s' probably isn't a %d-bit LSB-first ELF file.\n",
466 #endif /* not WORD_BIGENDIAN */
467              filename, SIZEOF_VOID_P * 8);
468      close(fd);
469      errno = ENOEXEC; /* Hm, is this the best errno code to use? */
470      return -1;
471    }
472 
473    if (ehdr->e_phentsize != sizeof(Elf_Phdr))
474    {
475      fprintf(stderr, "section size was read as %d, not %d!\n",
476             ehdr->e_phentsize, (int)sizeof(Elf_Phdr));
477      close(fd);
478      return -1;
479    }
480    return fd;
481 }
482 
483 int
elf_find_dynamic_section(int fd,Elf_Ehdr * ehdr,Elf_Phdr * phdr)484 elf_find_dynamic_section(int fd, Elf_Ehdr *ehdr, Elf_Phdr *phdr)
485 {
486   int i;
487   if (lseek(fd, ehdr->e_phoff, SEEK_SET) == -1)
488   {
489     perror ("positioning for sections");
490     return 1;
491   }
492 
493   for (i = 0; i < ehdr->e_phnum; ++i)
494   {
495     if (read(fd, phdr, sizeof(*phdr)) != sizeof(*phdr))
496     {
497       perror ("reading section header");
498       return 1;
499     }
500     if (phdr->p_type == PT_DYNAMIC)
501       break;
502   }
503   if (i == ehdr->e_phnum)
504     {
505       fprintf (stderr, "No dynamic section found.\n");
506       return 2;
507     }
508 
509   if (0 == phdr->p_filesz)
510     {
511       fprintf (stderr, "Length of dynamic section is zero.\n");
512       return 3;
513     }
514 
515   return 0;
516 }
517 
518 void
elf_close(int fd)519 elf_close(int fd)
520 {
521   close(fd);
522 }
523 
524 const char *
elf_tagname(int tag)525 elf_tagname(int tag)
526 {
527   switch (tag) {
528   case DT_RPATH:
529     return "RPATH";
530     break;
531 #if defined(DT_RUNPATH)
532   case DT_RUNPATH:
533     return "RUNPATH";
534     break;
535 #endif /* DT_RUNPATH */
536   }
537   return "UNKNOWN";
538 }
539 
540 int
elf_dynpath_tag(int tag)541 elf_dynpath_tag(int tag)
542 {
543   return ( tag == DT_RPATH
544 #if defined(DT_RUNPATH)
545            || tag == DT_RUNPATH
546 #endif /* DT_RUNPATH */
547            );
548 }
549 /*
550 Taken from another list:
551 
552 _Changing_ is a little tricky, but the attached program strips rpaths
553 from executables (I find it essential for debugging the binutils).
554 It's endian-dependent, if you want this for x86 you can just change
555 the occurrences of 'MSB' to 'LSB' and compile (I should really fix
556 that).
557 
558 --
559 Geoffrey Keating <geoffk@ozemail.com.au>
560 */
561 
562 #ifdef HAVE_CONFIG_H
563 //#  include "config.h"
564 #endif
565 
566 #include <stdio.h>
567 #include <unistd.h>
568 #include <fcntl.h>
569 #include <elf.h>
570 #if defined(HAVE_LINK_H)
571 #  include <link.h>
572 #endif /* HAVE_LINK_H */
573 #include <stdlib.h>
574 //#include "protos.h"
575 #include <string.h>
576 
577 /* Reads an ELF file, nukes all the RPATH entries. */
578 
579 int
killrpath(const char * filename)580 killrpath(const char *filename)
581 {
582    int fd;
583    Elf_Ehdr ehdr;
584    int i;
585    Elf_Phdr phdr;
586    Elf_Dyn *dyns;
587    int dynpos;
588 
589    fd = elf_open(filename, O_RDWR, &ehdr);
590 
591    if (fd == -1)
592    {
593      perror ("elf_open");
594      return 1;
595    }
596 
597    if (0 != elf_find_dynamic_section(fd, &ehdr, &phdr))
598    {
599      perror("found no dynamic section");
600      return 1;
601    }
602 
603    dyns = malloc(phdr.p_memsz);
604    if (dyns == NULL)
605      {
606        perror ("allocating memory for dynamic section");
607        return 1;
608      }
609    memset(dyns, 0, phdr.p_memsz);
610    if (lseek(fd, phdr.p_offset, SEEK_SET) == -1
611        || read(fd, dyns, phdr.p_filesz) != (int)phdr.p_filesz)
612      {
613        perror ("reading dynamic section");
614        return 1;
615      }
616 
617    dynpos = 0;
618    for (i = 0; dyns[i].d_tag != DT_NULL; ++i)
619      {
620        dyns[dynpos] = dyns[i];
621        if ( ! elf_dynpath_tag(dyns[i].d_tag) )
622         dynpos++;
623      }
624    for (; dynpos < i; ++dynpos)
625      dyns[dynpos].d_tag = DT_NULL;
626 
627    if (lseek(fd, phdr.p_offset, SEEK_SET) == -1
628        || write(fd, dyns, phdr.p_filesz) != (int)phdr.p_filesz)
629      {
630        perror ("writing dynamic section");
631        return 1;
632      }
633 
634    elf_close(fd);
635 
636    return 0;
637 }
638 /*
639  * Author: Petter Reinholdtsen <pere@hungry.com>
640  * date:   2001-01-20
641  *
642  * Alter ELF rpath information (insert, modify, remove).
643  *
644  * Based on source from Peeter Joot <peeterj@ca.ibm.com> and Geoffrey
645  * Keating <geoffk@ozemail.com.au>.
646  */
647 
648 #ifdef HAVE_CONFIG_H
649 //#  include "config.h"
650 #endif
651 
652 #include <stdio.h>
653 #include <stdlib.h>
654 #include <unistd.h>
655 #ifdef HAVE_GETOPT_H
656 #include <getopt.h>
657 #endif
658 //#include "protos.h"
659 
660 #ifdef HAVE_GETOPT_LONG
661 #  define GETOPT_LONG getopt_long
662 
663 static struct option long_options[] =
664 {
665   {"convert",   0, 0, 'c'},
666   {"delete",    0, 0, 'd'},
667   {"help",      0, 0, 'h'},
668   {"keepgoing", 0, 0, 'k'},
669   {"list",      0, 0, 'l'},
670   {"replace",   1, 0, 'r'},
671   {"version",   0, 0, 'v'}
672 };
673 
674 #else /* not HAVE_GETOPT_LONG */
675 #  define GETOPT_LONG(argc,argv,optstr,lopts,lidx) getopt(argc,argv,optstr)
676 #endif /* not HAVE_GETOPT_LONG */
677 
678 static void
usage(char * progname)679 usage(char *progname)
680 {
681   printf("Usage: %s [-v|-d|-c|-r <path>] <program> [<program> ...]\n\n",
682          progname);
683   printf("   -v|--version                Display program version number\n");
684   printf("   -d|--delete                 Delete current rpath/runpath setting\n");
685 #if defined(DT_RUNPATH)
686   printf("   -c|--convert                Convert rpath to runpath\n");
687 #endif /* DT_RUNPATH */
688   printf("   -r <path>|--replace <path>  Replace current rpath/runpath setting\n");
689   printf("                               with the path given\n");
690   printf("   -l|--list                   List the current rpath/runpath (default)\n");
691   printf("   -h|--help                   Show this usage information.\n");
692 #ifndef HAVE_GETOPT_LONG
693   printf("\n *** The long options are not available on this platform");
694 #endif /* not HAVE_GETOPT_LONG */
695 #if !defined(DT_RUNPATH)
696   printf("\n *** There is no support for runpath on this platform");
697 #endif /* DT_RUNPATH */
698   printf("\n");
699 }
700 
701 int
main(int argc,char * const argv[])702 main(int argc, char * const argv[])
703 {
704   int retval = 0;
705   int convert = 0;      /* convert to given type */
706   int remove = 0;       /* remove or not */
707   int keep_going = 0;   /* Break on first error, or keep going? */
708   char *newpath = NULL; /* insert this path */
709   int opt;
710 #ifdef HAVE_GETOPT_LONG
711   int option_index = 0;
712 #endif /* HAVE_GETOPT_LONG */
713 
714   if (argc < 2)
715     {
716       usage(argv[0]);
717       return 1;
718     }
719 
720   do {
721     opt = GETOPT_LONG(argc, argv, "cdhklr:v", long_options, &option_index);
722     switch (opt)
723       {
724 #if defined(DT_RUNPATH)
725       case 'c':
726         convert = 1;
727         break;
728 #endif /* DT_RUNPATH */
729       case 'd':
730         remove = 1;
731         break;
732       case 'k':
733         keep_going = 1;
734         break;
735       case 'r':
736         newpath = optarg;
737         break;
738       case 'v':
739         printf("%s version %s\n", PACKAGE, VERSION);
740         exit(0);
741         break;
742       case 'l': /* This is the default action */
743         newpath = NULL;
744         break;
745       case -1:
746         break;
747       default:
748         printf("Invalid argument '%c'\n", opt);
749       case 'h':
750         usage(argv[0]);
751         exit(0);
752         break;
753       }
754   } while (-1 != opt);
755 
756   while (optind < argc && (!retval || keep_going))
757     {
758       if (remove)
759         retval |= killrpath(argv[optind++]);
760       else
761         /* list by default, replace if path is set */
762         retval |= chrpath(argv[optind++], newpath, convert);
763     }
764 
765   return retval;
766 }
767