logo

utils-std

Collection of commonly available Unix tools git clone https://anongit.hacktivis.me/git/utils-std.git/

basename.c (2760B)


  1. // utils-std: Collection of commonly available Unix tools
  2. // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
  3. // SPDX-License-Identifier: MPL-2.0
  4. #define _POSIX_C_SOURCE 200809L
  5. #include "../config.h"
  6. #include "../libutils/getopt_nolong.h"
  7. #include <libgen.h> // basename
  8. #include <stdbool.h>
  9. #include <stdio.h> // puts, perror
  10. #include <string.h> // strlen, strncmp
  11. #include <unistd.h> // getopt
  12. #ifdef HAS_GETOPT_LONG
  13. #include <getopt.h>
  14. #endif
  15. const char *argv0 = "basename";
  16. #ifdef __GNUC__
  17. #define _NonNull __attribute__((nonnull))
  18. #else
  19. #define _NonNull
  20. #endif
  21. _NonNull static char *
  22. suffix_basename(char *name, char *suffix)
  23. {
  24. char *string = basename(name);
  25. size_t suflen = strlen(suffix);
  26. size_t len = strlen(string);
  27. if(suflen < len && strcmp(&string[len - suflen], suffix) == 0)
  28. {
  29. string[len - suflen] = '\0';
  30. }
  31. return string;
  32. }
  33. static void
  34. usage(void)
  35. {
  36. fputs("Usage: basename [-z] [path] [suffix]\n"
  37. " basename [-az] [-s suffix] [path...]\n",
  38. stderr);
  39. }
  40. int
  41. main(int argc, char *argv[])
  42. {
  43. bool opt_a = false;
  44. char *suffix = NULL;
  45. char delim = '\n';
  46. #ifdef HAS_GETOPT_LONG
  47. // Strictly for GNUisms compatibility so no long-only options
  48. // clang-format off
  49. static struct option opts[] = {
  50. {"multiple", no_argument, NULL, 'a'},
  51. {"suffix", required_argument, NULL, 's'},
  52. {"zero", no_argument, NULL, 'z'},
  53. {0, 0, 0, 0},
  54. };
  55. // clang-format on
  56. // Need + as first character to get POSIX-style option parsing
  57. for(int c = -1; (c = getopt_long(argc, argv, "+:as:z", opts, NULL)) != -1;)
  58. #else
  59. for(int c = -1; (c = getopt_nolong(argc, argv, ":as:z")) != -1;)
  60. #endif
  61. {
  62. switch(c)
  63. {
  64. case 'a':
  65. opt_a = true;
  66. break;
  67. case 's':
  68. opt_a = true;
  69. suffix = optarg;
  70. break;
  71. case 'z':
  72. delim = '\0';
  73. break;
  74. case ':':
  75. fprintf(stderr, "%s: error: Missing operand for option '-%c'\n", argv0, optopt);
  76. usage();
  77. return 1;
  78. case '?':
  79. GETOPT_UNKNOWN_OPT
  80. usage();
  81. return 1;
  82. }
  83. }
  84. argc -= optind;
  85. argv += optind;
  86. if(!opt_a || argc == 0)
  87. {
  88. int ret = 0;
  89. switch(argc)
  90. {
  91. case 0:
  92. ret = printf(".%c", delim);
  93. break;
  94. case 1:
  95. ret = printf("%s%c", basename(argv[0]), delim);
  96. break;
  97. case 2:
  98. ret = printf("%s%c", suffix_basename(argv[0], argv[1]), delim);
  99. break;
  100. default:
  101. usage();
  102. return 1;
  103. }
  104. if(ret < 0)
  105. {
  106. perror("basename: error: Failed to print result");
  107. return 1;
  108. }
  109. return 0;
  110. }
  111. for(int argi = 0; argi < argc; argi++)
  112. {
  113. char *res = NULL;
  114. if(suffix != NULL)
  115. res = suffix_basename(argv[argi], suffix);
  116. else
  117. res = basename(argv[argi]);
  118. if(printf("%s%c", res, delim) < 0)
  119. {
  120. perror("basename: error: Failed to print result");
  121. return 1;
  122. }
  123. }
  124. return 0;
  125. }