Simple Playlist ModifierThis program is a search and replace program for text based files. The main goal of the program is to replace directory paths to music files.The idea is to have the program read files from input and then validate the given parameters (a file path in this case).The Source M3U Contains -- created by VLC#EXTM3U#EXTINF:330,Martin Solveig, Dragonette & Idoling!!! - Big in Japan (Les Bros Remix)C:\User\Name\Music\Singles & Misc\House\02 Big in Japan (Les Bros Remix).mp3... ... and many, many, more songs.I wanted all of the files to read as below without altering each one painstakingly in File Explorer, Finder, or Nautilus.The Modified M3U Contents -- modified with plist#EXTM3U#EXTINF:330,Martin Solveig, Dragonette & Idoling!!! - Big in Japan (Les Bros Remix)D:\Music\Singles & Misc\House\02 Big in Japan (Les Bros Remix).mp3... ... and many, many, more songs. you get the point.I needed a way to edit large playlists with ease and speed. So I made this program to help manage that.There are 3 distinct Arguments and each is an attribute of its base option: the source file, source file path, and target file path.As an example:plist [file name] [source file path pattern] [target file path pattern]plist mymusic.m3u c:\users\username\Music d:\MusicThink of it like strstr() with the haystack and needle, only I'm searching literally strand by strand for the needles I'm looking for and doing it for all occurrences, not just the first occurrence found.This program is built with VLC M3U playlists in mind and no other type. The program does produce a backup file before processing and making alterations.If all else fails, just delete the file extension .backup so that it reads .m3u only. The backup files are truncated with each use, but does generate an identifier based on the original files name. So be cautious.I used MinGW to compile it.Compiled as: $ gcc -std=c99 -Wall -Wextra -Wpedantic convert_plist.c -o plist#include <stdio.h>#include <stdlib.h>#include <assert.h>#include <string.h>#ifdef BUFSIZ    #undef BUFSIZ    #define BUFSIZ 1000 #endif// if more symbols should be addressed, adjust as neccessary.// for example, use an array if multiple characters are to be // utilized as tokens to ignore or tokens to acknowledge...const int BAD_INPUT = -1; //return code for bad inputconst int BAD_BAKUP = -2; //--- --- bad backup fileconst int BAD_BFMOD = -3; //--- --- bad backup file modificationconst int IGN_ITEMS = 1; //max number of ignored tokensconst int ACK_ITEMS = 6; //max number of acknowledged tokensconst int EXT_ITEMS = 4; //max number of file extensionstypedef _Bool bool; //typedef to prettify //define bool keywords true and falseconst bool true = 1;const bool false = 0;//use lables to differentiate argument typesenum ARGUMENT { self = 1, source, target, met };const char * m3u = .m3u;void rmnewline(char * expression){//remove the newline character        char * newline = NULL;        size_t position, copylen;        size_t stringlen = strlen(expression);        if (NULL != (newline = strrchr(expression, '\n')))        {//remove the newline if expression is valid            copylen = strlen(newline);            position = stringlen - copylen;            expression[position] = '\0';        }   }bool valid_args(int argc){//validate the number of correct arguments    bool status = false;    switch(argc)    {//assume at least one argument is missing, else all arguments are met (since 4 are valid)        case self:             puts(\nplist - Portable Playlist Modifier\n                 This program replaces the Source File Path Pattern with the Target Path Pattern.\n\n                 Usage: plist [file_name] [source_path_pattern] [target_path_pattern]\n\n                 [file_name] is the playlist file to process.\n                 [source_path_pattern] is the string pattern to be replaced.\n                 [target_path_pattern] is the string pattern that replaces the source pattern.\n\n                 Example: plist /path/to/my/playlist.m3u /current/path/to/my/music /new/path/to/my/music\n                 Use M3U playlist files, or modify at your own expense to manage other playlist types.);            break;        case source:            puts(No [source_path_pattern] was provided.);            puts(Invoke [plist] without arguments for help.);            break;        case target:            puts(No [target_path_pattern] was provided.);            puts(Invoke [plist] without arguments for help.);            break;        case met:            status = true;            break;        default:            puts(Invalid use of [plist].);            puts(Invoke [plist] without arguments for help.);            break;    }    return status;}bool valid_m3u_ext(const char * filename){//validate the m3u file extension exclusively    bool status = false;    char * container = strrchr(filename, '.');    (!strcmp(container, m3u)) ? status = true : puts(Invalid argument(s) supplied.);    return status;}char * mod_filename(const char * filename){//modify the file name so that it contains a .backup extension    size_t nullchar = 1;    char * temp = malloc(strlen(filename) + strlen(.backup) + nullchar);    assert(NULL != temp);    strcpy(temp, filename);    rmnewline(temp); //if it does not find a newline, it does nothing    strcat(temp, .backup);    return temp;}void copy_playlist(const char * filename, FILE * playlist, FILE * backup){//copy the file contents and report the number of bytes counted    int ch, count = 0;    while ((ch = getc(playlist)) != EOF)    {        putc(ch, backup);        ++count;    }    printf(Backup created successfully: %s\n, filename);     printf(%d bytes were copied there.\n\n, count);}char * backup_playlist(char * filename){//backup the playlist file    char * temp = mod_filename(filename);    //open playlist file for read-only    FILE * playlist = fopen(filename, r);    //create a new file for writing and updating    FILE * backup = fopen(temp, w);    if (NULL == playlist || NULL == backup)    {        puts(Failed to open and backup file(s).);        return NULL;    }    copy_playlist(temp, playlist, backup);    //close the opened files    fclose(playlist);    fclose(backup);    return temp;}void copy_metadata(FILE * newplist, FILE * oldplist){    int ch;    putc('#', newplist); //make sure you add the newline!    while ('\n' != (ch = getc(oldplist)))     {//if the line starts with a '#', copy it over        putc(ch, newplist);    }    putc('\n', newplist);}void compare_src_txt(char * source, FILE * oldplist){//compare source text and if it matches, skip over it    int ch;    for (size_t i = 1; i < strlen(source); i++)    {        if ((ch = getc(oldplist)) != source[i])         {            break;        }    }}void write_target_txt(char * target, FILE * newplist){//write the target text to where the source text was    for (size_t i = 0; i < strlen(target); i++)    {        putc(target[i], newplist);    }}void cat_src_txt(FILE * newplist, FILE * oldplist){//concatenate the rest of the source text to the file    int ch;    while ('\n' != (ch = getc(oldplist)))    {//copy over the rest of the source text        putc(ch, newplist);    }    putc('\n', newplist); //make sure you add the newline!}bool char_filter(char * source, char * target, FILE * newplist, FILE * oldplist){//read file character by character -- tedious, i know, but precise.    int ch;    while (EOF != (ch = getc(oldplist)))    {        switch(ch)        {            case '#':                copy_metadata(newplist, oldplist);                break;            case '/': //root for unix            case 'c': //c:\ for windows            case 'C':            case 'd': //d:\ for windows            case 'D':                   compare_src_txt(source, oldplist);                write_target_txt(target, newplist);                cat_src_txt(newplist, oldplist);                break;            default:                return false;        }    }    return true;}bool modify_playlist(char * filename, char * backup, char * source, char * target){//modify the playlist using the given source, and target, text    //open playlist file for write-only    FILE * newplist = fopen(filename, w);    //use the backup for read-only    FILE * oldplist = fopen(backup, r);    if (NULL == newplist || NULL == oldplist)    {//make sure the files were opened        puts(Failed to open and backup file(s).);        return false;    }    if (!char_filter(source, target, newplist, oldplist))    {        puts(Invalid initializing of a character after newline.);        return false;    }    fclose(newplist);    fclose(oldplist);    printf(%s was modified successfully!\n, filename);    return true;}int main(int argc, char * argv[]){    if (!valid_args(argc))    {//must have at least one argument        return BAD_INPUT;    }    if (!valid_m3u_ext(argv[1]))    {//must be a defined file extension type        return BAD_INPUT;    }    //each pointer now has a given name instead of argv[SOME_VALUE]    char * filename = argv[1];    char * source = argv[2];    char * target = argv[3];    char * backup = backup_playlist(filename);    if (NULL == backup)    {//backup must be good before moving on to writing data        return BAD_BAKUP;    }    if (!modify_playlist(filename, backup, source, target))    {        return BAD_BFMOD;    }    return 0;}
Searching and replacing text
c;file;io;search
Simplify rmnewlineCurrently, rmnewline() does some complicated math to remove the last newline character:void rmnewline(char * expression){//remove the newline character        char * newline = NULL;        size_t position, copylen;        size_t stringlen = strlen(expression);        if (NULL != (newline = strrchr(expression, '\n')))        {//remove the newline if expression is valid            copylen = strlen(newline);            position = stringlen - copylen;            expression[position] = '\0';        }   }All the math is simply figuring out where the newline character is, which is being pointed to by newline.  So all you really have to do is:void rmnewline(char * expression){//remove the newline character    char * newline = strrchr(expression, '\n');    if (NULL != newline)        *newline = '\0';}Copying a fileYour copy_playlist() function copies the playlist character by character, returning the size of the file.  You could do better by:Using fseek() and ftell() to measure the size of the file.Using fread() and fwrite() to copy the file in blocks (or even in one big block) instead of character by character.The filterAlthough the search and replace filter works if all the files belong to the same path, it doesn't make sure that the source path matches the expected path.  It just skips over any part of the source path that matches the expected path.  You could do better by reading one whole line from the source file, comparing its prefix with the expected source path, and then replacing the prefix if it matches.  If it doesn't match, you can just output the line unchanged.