Lets say you had a sort function that takes a function as a parameter which implements the "compare" functionality of the sort.  The sort would then be capable of sorting a list of any arbitrary struct, by handing it a comparer function that implements the correct order for your particular struct.
void bubbleSort(Node* start, bool comparerFunction(void* a, void* b))
Consider the following struct definition:
typedef struct {
   int  book_id;
   char title[50];
   char author[50];
   char subject[100];
   char ISBN[13];
} Book;
And this unremarkable linked list definition:
typedef struct node{
  void* item;
  struct node* next;
} Node;
Which can store an arbitrary struct in the item member.
Because you know the type of the members you've placed in your linked list, you can write a comparer function that will do the right thing:
bool sortByTitle(void* left, void* right) {
  Book* a = (Book*)left;
  Book* b = (Book*)right;
 
  return strcmp(a->title, b->title) > 0;
}
And then call your sort like this:
bubbleSort(myList, sortByTitle);
For completeness, here is the bubbleSort implementation:
/* Bubble sort the given linked list */
void bubbleSort(Node *start, bool greaterThan(void* a, void* b)) 
{ 
    int swapped, i; 
    Node* ptr1; 
    Node* lptr = NULL; 
    /* Checking for empty list */
    if (start == NULL) 
        return; 
    do
    { 
        swapped = 0; 
        ptr1 = start; 
        while (ptr1->next != lptr) 
        { 
            if (greaterThan(ptr1->item, ptr1->next->item))
            { 
                swap(ptr1, ptr1->next); 
                swapped = 1; 
            } 
            ptr1 = ptr1->next; 
        } 
        lptr = ptr1; 
    } 
    while (swapped); 
} 
/* function to swap data of two nodes a and b*/
void swap(Node *a, Node *b) 
{ 
    void* temp = a->item; 
    a->item = b->item; 
    b->item = temp; 
}