File information

Gain knowledge of file system operations, such as navigating directories, creating and deleting files and directories, and modifying file permissions. Implement the necessary functionalities to handle these operations within your shell.

The stat (man 2 stat) system call gets the status of a file. On success, zero is returned. On error, -1 is returned.

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

/**
 * main - stat example
 *
 * Return: Always 0.
 */
int main(int ac, char **av)
{
    unsigned int i;
    struct stat st;

    if (ac < 2)
    {
        printf("Usage: %s path_to_file ...\n", av[0]);
        return (1);
    }
    i = 1;
    while (av[i])
    {
        printf("%s:", av[i]);
        if (stat(av[i], &st) == 0)
        {
            printf(" FOUND\n");
        }
        else
        {
            printf(" NOT FOUND\n");
        }
        i++;
    }
    return (0);
}

Write a program that looks for files in the current PATH.

  • Usage: _which filename ...

      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
    
      #define MAX_PATH_LENGTH 1024
    
      // Function to check if a file exists in a directory
      int fileExists(const char *filename, const char *directory) {
          char path[MAX_PATH_LENGTH];
          snprintf(path, sizeof(path), "%s/%s", directory, filename);
          FILE *file = fopen(path, "r");
          if (file != NULL) {
              fclose(file);
              return 1;  // File exists
          }
          return 0;  // File does not exist
      }
    
      int main(int argc, char *argv[]) {
          if (argc < 2) {
              printf("Usage: %s filename ...\n", argv[0]);
              return 1;
          }
    
          char *path_env = getenv("PATH");
          if (path_env == NULL) {
              printf("Unable to access PATH environment variable.\n");
              return 1;
          }
    
          char *path = strtok(path_env, ":");
          int found = 0;
    
          // Iterate through each directory in PATH
          while (path != NULL) {
              int i;
              for (i = 1; i < argc; i++) {
                  if (fileExists(argv[i], path)) {
                      printf("%s\n", path);
                      found = 1;
                  }
              }
              path = strtok(NULL, ":");
          }
    
          if (!found) {
              printf("Files not found in the current PATH.\n");
          }
    
          return 0;
      }
    

    In this program:

    • The fileExists function checks if a file with the provided filename exists in the specified directory. It constructs the full file path by concatenating the directory and filename, and then attempts to open the file for reading. If the file can be opened, it means the file exists.

    • In the main function, the program expects at least one command-line argument (in addition to the program name). If the argument count is less than 2, it displays the usage message and returns.

    • The program uses the getenv function to retrieve the value of the PATH environment variable. It checks if the value is NULL and displays an error message if it is unable to access the PATH variable.

    • The program tokenizes the PATH value using strtok with ":" as the delimiter to extract each directory path.

    • For each directory in the PATH, the program checks if each provided filename exists in that directory using the fileExists function. If a file is found, the directory path is printed.

    • Finally, if no files are found in the current PATH, a message is displayed to indicate that the files were not found.

You can compile and run the program, providing the filenames you want to search for in the current PATH as command-line arguments. For example:

    $ ./program_name file1 file2

The program will search for the provided files in the directories specified by the PATH environment variable and print the directories where the files are found.

Environment

We have seen earlier that the shell uses an environment list, where environment variables are “stored”. The list is an array of strings, with the following format: var=value, where var is the name of the variable and value its value. As a reminder, you can list the environment with the command printenv.

Actually, every process comes with an environment. When a new process is created, it inherits a copy of its parent’s environment. To access the entire environment within a process, you have several options:

  • via the main function

  • via the global variable environ

Print environment using environ

#include <stdio.h>
#include <stdlib.h>

extern char **environ;

int main() {
    char **env = environ;
    while (*env != NULL) {
        printf("%s\n", *env);
        env++;
    }

    return 0;
}

Compare env and environ addresses

#include <stdio.h>
#include <stdlib.h>

extern char **environ;

int main(int argc, char *argv[], char *env[]) {
    printf("Address of env: %p\n", env);
    printf("Address of environ: %p\n", environ);

    return 0;
}

Implement _getenv function

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern char **environ;

char *_getenv(const char *name) {
    char **env = environ;
    size_t name_len = strlen(name);

    while (*env != NULL) {
        if (strncmp(*env, name, name_len) == 0 && (*env)[name_len] == '=') {
            return *env + name_len + 1;
        }
        env++;
    }

    return NULL;
}

int main() {
    char *value = _getenv("PATH");
    if (value != NULL) {
        printf("Value of PATH: %s\n", value);
    } else {
        printf("PATH environment variable not found.\n");
    }

    return 0;
}

Print each directory in the PATH environment variable

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern char **environ;

void printPathDirectories() {
    char *path = getenv("PATH");
    if (path != NULL) {
        char *dir = strtok(path, ":");
        while (dir != NULL) {
            printf("%s\n", dir);
            dir = strtok(NULL, ":");
        }
    } else {
        printf("PATH environment variable not found.\n");
    }
}

int main() {
    printPathDirectories();

    return 0;
}

Build a linked list of directories in the PATH environment variable

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern char **environ;

typedef struct Node {
    char *directory;
    struct Node *next;
} Node;

Node *buildPathList() {
    char *path = getenv("PATH");
    if (path == NULL) {
        printf("PATH environment variable not found.\n");
        return NULL;
    }

    Node *head = NULL;
    Node *prev = NULL;

    char *dir = strtok(path, ":");
    while (dir != NULL) {
        Node *node = malloc(sizeof(Node));
        if (node == NULL) {
            printf("Memory allocation failed.\n");
            break;
        }

        node->directory = strdup(dir);
        node->next = NULL;

        if (prev == NULL) {
            head = node;
        } else {
            prev->next = node;
        }

        prev = node;
        dir = strtok(NULL, ":");
    }

    return head;
}

void freePathList(Node *head) {
    Node *curr = head;
    while (curr != NULL) {
        Node *next = curr->next;
        free(curr->directory);
        free(curr);
        curr = next;
    }
}

int main() {
    Node *pathList = buildPathList();
    if (pathList != NULL) {
        Node *curr = pathList;
        while (curr != NULL) {
            printf("%s\n", curr->directory);
            curr = curr->next;
        }
        freePathList(pathList);
    }

    return 0;
}

Implement _setenv function

cCopy code#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern char **environ;

int _setenv(const char *name, const char *value, int overwrite) {
    if (name == NULL || value == NULL) {
        return -1;  // Invalid arguments
    }

    size_t name_len = strlen(name);
    size_t value_len = strlen(value);
    char **env = environ;

    while (*env != NULL) {
        if (strncmp(*env, name, name_len) == 0 && (*env)[name_len] == '=') {
            if (overwrite) {
                // Overwrite existing value
                size_t entry_len = name_len + value_len + 2;
                char *entry = malloc(entry_len);
                if (entry == NULL) {
                    return -1;  // Memory allocation failed
                }
                snprintf(entry, entry_len, "%s=%s", name, value);
                *env = entry;
                return 0;
            } else {
                // Value exists, but overwrite is not allowed
                return 0;
            }
        }
        env++;
    }

    // Value does not exist, create a new entry
    size_t entry_len = name_len + value_len + 2;
    char *entry = malloc(entry_len);
    if (entry == NULL) {
        return -1;  // Memory allocation failed
    }
    snprintf(entry, entry_len, "%s=%s", name, value);

    // Count the number of existing environment variables
    int env_count = 0;
    env = environ;
    while (*env != NULL) {
        env_count++;
        env++;
    }

    // Allocate memory for the new environment variable
    char **new_env = realloc(environ, (env_count + 2) * sizeof(char *));
    if (new_env == NULL) {
        free(entry);
        return -1;  // Memory allocation failed
    }
    environ = new_env;
    environ[env_count] = entry;
    environ[env_count + 1] = NULL;

    return 0;
}

int main() {
    char *name = "MY_VAR";
    char *value = "my_value";

    _setenv(name, value, 1);  // Overwrite is allowed

    char *result = getenv(name);
    if (result != NULL) {
        printf("%s=%s\n", name, result);
    } else {
        printf("Environment variable not found.\n");
    }

    return 0;
}

Implement _unsetenv function

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern char **environ;

int _unsetenv(const char *name) {
    if (name == NULL) {
        return -1;  // Invalid argument
    }

    size_t name_len = strlen(name);
    char **env = environ;

    while (*env != NULL) {
        if (strncmp(*env, name, name_len) == 0 && (*env)[name_len] == '=') {
            // Shift the remaining environment variables to fill the gap
            char **next = env + 1;
            while (*next != NULL) {
                *env = *next;
                env = next;
                next++;
            }
            *env = NULL;  // Set the last entry to NULL
            return 0;
        }
        env++;
    }

    return 0;  // Variable not found, nothing to unset
}

int main() {
    char *name = "MY_VAR";

    _unsetenv(name);

    char *result = getenv(name);
    if (result != NULL) {
        printf("%s=%s\n", name, result);
    } else {
        printf("Environment variable not found.\n");
    }

    return 0;
}