Guest User

Untitled

a guest
Apr 19th, 2018
77
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 7.08 KB | None | 0 0
  1. /* morg.c */
  2. /*
  3.  * This file is public domain as declared by Sturm Mabie.
  4.  */
  5.  
  6. #include <sys/stat.h>
  7. #include <sys/types.h>
  8.  
  9. #include <dirent.h>
  10. #include <err.h>
  11. #include <errno.h>
  12. #include <fcntl.h>
  13. #include <getopt.h>
  14. #include <libgen.h>
  15. #include <unistd.h>
  16.  
  17. #include <limits.h>
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21.  
  22. #include <taglib/tag_c.h>
  23.  
  24. #define FMTSTR                              \
  25.     "/home/\'\"$USER\"\'/music/%artist/%artist - %album/%track %title.%type"
  26. #define CPSTR   "mkdir -p \"`dirname '%dst'`\" && cp '%src' '%dst'"
  27. #define REPLACESTR "/_"
  28.  
  29. #ifdef __GLIBC__
  30. #undef basename
  31. #undef dirname
  32. #define basename bsd_basename
  33. #define dirname bsd_dirname
  34.  
  35. char *bsd_basename(const char *);
  36. char *bsd_dirname(const char *);
  37. size_t strlcat(char *, const char *, size_t);
  38. size_t strlcpy(char *, const char *, size_t);
  39. #endif  /* __GLIBC__ */
  40.  
  41. int copy_file(const char *);
  42. int find_files(const char *);
  43.  
  44. char *make_path(TagLib_Tag *, const char *);
  45. char *make_copy(const char *, const char *, const char *);
  46. char *replace(char *);
  47.  
  48. void copy_escape(char *, const char *);
  49. int count_quotes(const char *);
  50.  
  51. void usage();
  52.  
  53. char *fmtstr, *cpstr, *replacestr;
  54. int vflg;
  55.  
  56. int
  57. main(int argc, char **argv)
  58. {
  59.     int c;
  60.    
  61.     vflg = 0;
  62.     if ((fmtstr = getenv("MORGFMT")) == NULL)
  63.         fmtstr = FMTSTR;
  64.     if ((cpstr = getenv("MORGCP")) == NULL)
  65.         cpstr = CPSTR;
  66.     if ((replacestr = getenv("MORGREPLACE")) == NULL)
  67.         replacestr = REPLACESTR;
  68.     while ((c = getopt(argc, argv, "s:t:v")) != -1) {
  69.         switch (c) {
  70.         case 's':   /* format string specified */
  71.             fmtstr = optarg;
  72.             break;
  73.         case 't':   /* transfer command specified */
  74.             cpstr = optarg;
  75.             break;
  76.         case 'v':   /* verbose mode */
  77.             vflg = 1;
  78.             break;
  79.         default:
  80.             usage();
  81.         }
  82.     }
  83.     argc -= optind;
  84.     argv += optind;
  85.  
  86.     if (argc < 1)
  87.         usage();
  88.     do {
  89.         (void)find_files(*argv);
  90.     } while (*++argv != NULL);
  91.     return 0;
  92. }
  93.  
  94. int
  95. find_files(const char *path)
  96. {
  97.     struct stat sb;
  98.     DIR *dirp;
  99.     struct dirent *dp;
  100.     static char buf[PATH_MAX];
  101.     size_t s;
  102.  
  103.     if ((dirp = opendir(path)) == NULL) {
  104.         if (errno == ENOTDIR) {
  105.             (void)copy_file(path);
  106.             return 0;
  107.         } else {
  108.             warn("opendir: %s", path);
  109.             return 1;
  110.         }
  111.     }
  112.     if ((s = strlcpy(buf, path, PATH_MAX)) >= PATH_MAX)
  113.         goto longer;
  114.     if (path[(s = strlen(path)) - 1] != '/') {
  115.         buf[s] = '/';
  116.         buf[s + 1] = '\0';
  117.     }
  118.  
  119.     errno = 0;
  120.     while ((dp = readdir(dirp)) != NULL) {
  121.         if (strcmp(dp->d_name, ".") == 0 ||
  122.             strcmp(dp->d_name, "..") == 0)
  123.             continue;
  124.         if (strlcat(buf, dp->d_name, PATH_MAX) >= PATH_MAX)
  125.             goto longer;
  126.         if (stat(buf, &sb) == -1) {
  127.             warn("stat: %s", buf);
  128.             goto er;
  129.         }
  130.         if (S_ISDIR(sb.st_mode) || S_ISLNK(sb.st_mode))
  131.             (void)find_files(buf);
  132.         else
  133.             (void)copy_file(buf);
  134.         buf[buf[s] == '/' ? s + 1 : s] = '\0';
  135.     }
  136.     if (errno) {
  137.         warn("readdir");
  138.         goto er;
  139.     }
  140.  
  141.     (void)closedir(dirp);
  142.     return 0;
  143. longer:
  144.     warnx("PATH_MAX of %d violated", PATH_MAX);
  145. er:
  146.     (void)closedir(dirp);
  147.     return 1;
  148. }
  149.  
  150. int copy_file(const char *file)
  151. {
  152.     static char real[PATH_MAX];
  153.     TagLib_File *tag_file;
  154.     char *p, *name, ext[5];
  155.    
  156.     (void)strlcpy(real, file, PATH_MAX);
  157.     if ((p = strrchr(real, '.')) == NULL)
  158.         return 1;
  159.     *p = '/';
  160.     if ((p = basename(real)) == NULL) {
  161.         warn("basename: %s", real);
  162.         return 1;
  163.     }
  164.     if (strcmp(p, "mp3") != 0 &&
  165.         strcmp(p, "flac") != 0 &&
  166.         strcmp(p, "ogg") != 0)
  167.         return 0;
  168.     (void)strlcpy(ext, p, sizeof(ext));
  169.     if ((name = basename(file)) == NULL) {
  170.         warn("basename: %s", file);
  171.         return 1;
  172.     }
  173.     if ((tag_file = taglib_file_new(file)) == NULL) {
  174.         warnx("the type of %s cannot be determined or the file cannot "
  175.               "be opened", name);
  176.         return 1;
  177.     }
  178.     if (!taglib_file_is_valid(tag_file)) {
  179.         warnx("the file %s is not valid", name);
  180.         taglib_file_free(tag_file);
  181.         return 1;
  182.     }
  183.     if (vflg) {
  184.         if (realpath(file, real) == NULL) {
  185.             warn("realpath: %s", file);
  186.             return 1;
  187.         }
  188.         (void)fprintf(stderr, "%s\n", real);
  189.     }
  190.     (void)system(make_copy(cpstr, file,
  191.                    make_path(taglib_file_tag(tag_file), ext)));
  192.  
  193.     taglib_tag_free_strings();
  194.     taglib_file_free(tag_file);
  195.     return 0;
  196. }
  197.  
  198. char *
  199. make_copy(const char *fmt, const char *src, const char *dst)
  200. {
  201.     static char ret[2048];
  202.     const char *p;
  203.     int d;
  204.     size_t len;
  205.  
  206.     for (p = fmt, d = 0; *p != '\0' && d < sizeof(ret); p++) {
  207.         if (*p == '%') {
  208.             if (*(p + 1) == '%')
  209.                 ret[d++] = *p;
  210.             else if (strncmp(p + 1, "src", 3) == 0) {
  211.                 if (strlen(ret) + count_quotes(src) + (len = strlen(src))
  212.                 copy_escape(&ret[d], src);
  213.                 /* (void)strlcpy(&ret[d], src, sizeof(ret)); */
  214.                 d += strlen(src);
  215.                 p += 3;
  216.             } else if (strncmp(p + 1, "dst", 3) == 0) {
  217.                 copy_escape(&ret[d], src);
  218.                 /* (void)strlcpy(&ret[d], dst, sizeof(ret)); */
  219.                 d += strlen(dst);
  220.                 p += 3;
  221.             }
  222.         } else
  223.             ret[d++] = *p;
  224.     }
  225.     ret[d] = '\0';
  226.     return ret;
  227. }
  228.  
  229. char *
  230. make_path(TagLib_Tag *tags, const char *type)
  231. {
  232. #define SAFE(s) (strlen(s) != 0 ? (s) : " ")
  233.     static char ret[2048];
  234.     static char yearbuf[5];
  235.     static char trackbuf[3];
  236.     char *p;
  237.     int d, c;
  238.     struct {
  239.         char *s;
  240.         char *p;
  241.     } table[] = {
  242.         { "track", NULL },
  243.         { "title", NULL },
  244.         { "artist",NULL },
  245.         { "album", NULL },
  246.         { "genre", NULL },
  247.         { "year",  NULL },
  248.         { "type",  NULL }
  249.     };
  250.  
  251.     (void)snprintf(trackbuf, 3, taglib_tag_track(tags) < 10 ? "0%d" : "%d",
  252.                taglib_tag_track(tags));
  253.     (void)snprintf(yearbuf, 5, "%d", taglib_tag_year(tags));
  254.     table[0].p = trackbuf;
  255.     table[1].p = replace(SAFE(taglib_tag_title(tags)));
  256.     table[2].p = replace(SAFE(taglib_tag_artist(tags)));
  257.     table[3].p = replace(SAFE(taglib_tag_album(tags)));
  258.     table[4].p = replace(SAFE(taglib_tag_genre(tags)));
  259.     table[5].p = yearbuf;
  260.     table[6].p = (char *)type;
  261.  
  262.     for (p = fmtstr, d = 0; *p != '\0' && d < sizeof(ret); p++) {
  263.         if (*p == '%') {
  264.             if (*(p + 1) == '%') {
  265.                 ret[d++] = *p;
  266.                 continue;
  267.             }
  268.             for (c = 0; c < sizeof(table) / sizeof(*table); c++) {
  269.                 if (strncmp(p + 1, table[c].s,
  270.                         strlen(table[c].s)) == 0) {
  271.                     d += strlcpy(&ret[d],
  272.                              table[c].p, sizeof(ret));
  273.                     p += strlen(table[c].s);
  274.                     break;
  275.                 }
  276.             }
  277.         } else
  278.             ret[d++] = *p;
  279.     }
  280.     ret[d] = '\0';
  281.  
  282.     free(table[1].p);
  283.     free(table[2].p);
  284.     free(table[3].p);  
  285.     free(table[4].p);
  286.     return ret;
  287. }
  288.  
  289. char *
  290. replace(char *s)
  291. {
  292.     int c;
  293.     size_t len;
  294.     char *ret, *p;
  295.  
  296.     if ((ret = strdup(s)) == NULL)
  297.         err(1, "strdup");
  298.  
  299.     if ((len = strlen(replacestr)) % 2 != 0)
  300.         warnx("MORGREPLACE length not even, performing no replacements");
  301.     else {
  302.         for (p = ret; *p != '\0'; p++) {
  303.             for (c = 0; c < len; c += 2) {
  304.                 if (*p == replacestr[c])
  305.                     *p = replacestr[c + 1];
  306.             }
  307.         }
  308.     }
  309.     return ret;
  310. }
  311.  
  312. void copy_escape(char *dst, const char *src)
  313. {
  314.     while (*src != '\0') {
  315.         if (*src++ == '\'') {
  316.             *dst++ = '\\';
  317.             *dst++ = '\'';
  318.         } else
  319.             *dst++ = *src++;
  320.     }
  321.     *dst = '\0';
  322. }
  323.  
  324. int count_quotes(const char *s)
  325. {
  326.     int ret;
  327.  
  328.     ret = 0;
  329.     while (*s++ != '\0') {
  330.         if (*s == '\'')
  331.             ret++;
  332.     }
  333.     return ret;
  334. }
  335.  
  336. void
  337. usage()
  338. {
  339.     (void)fprintf(stderr, "morg [-v] [-s fmtstr] [-t cpstr] file ...\n");
  340.     exit(1);
  341. }
Add Comment
Please, Sign In to add comment