2

I'm writing a simple shell in C. Part of this shell uses bash commands that need to be split up into an array of characters, so that I can properly execute the commands. As a simple proof of concept, I wanted to make a C program that would check to see if its a word, or a special character (|, &, >, and <).

Here is an example of how it should look:

input a command: cat hello.txt |wc -l
---output---
cat
hello.txt
|
wc
-l

So far, I've gotten every case to work, for example except for one, and that is when I do a special character like in |wc l where a letter is after the special character. Here is what my current output looks like:

input a command: cat hello.txt |wc -l
---output---
cat
hello.txt
|wc
-l

Below is my code, I've tried adding special cases, removing cases, and nothing seems to work correctly. How should I solve this?

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

#define ALPHABET 1000
#define LESS_THAN 60
#define GREATER_THAN 62
#define AMPERSAND 38
#define PIPE 124
#define WHITESPACE 32
#define NEWLINE 10
#define MYCOPOUT 9999

#define CAPITALS(i) ((i >= 65) && (i <= 90))
#define LOWERS(i) ((i >= 97) && (i <= 122))
#define BACKSLASH(i) i == 47

int classify(int i)
{
    if (CAPITALS(i) || LOWERS(i) || BACKSLASH(i))
        return ALPHABET;
    return i;
}

char isSpecial(int i)
{
    return (i == LESS_THAN) ||(i == GREATER_THAN) || (i == AMPERSAND) || (i == PIPE);
}

char isWhiteSpace(int i)
{
    return (i == WHITESPACE) || (i == NEWLINE);
}

void PRINT_N_NEW(int c, int last)
{
    if (isWhiteSpace(last)) {
        printf("%c",c);
    } else if(isSpecial(last)) {
        printf("\n%c\n",c);
    } else {
        printf("%c\n", c);
    }
    last = c;
}

int main(int argc, char** argv)
{

    int i;
    int last = MYCOPOUT;
    while ( (i = getchar()) != EOF){
        switch(classify(i)){
            case ALPHABET:
                printf("%c",i);
                last = i;
                break;
            case LESS_THAN:
                PRINT_N_NEW(i,last);
                break;
            case GREATER_THAN:
                PRINT_N_NEW(i,last);
                break;
            case AMPERSAND:
                PRINT_N_NEW(i,last);
                break;
            case PIPE:
                PRINT_N_NEW(i,last);
                break;
            case WHITESPACE:
                printf("\n");
                last = i;
                break;
            case NEWLINE:
                printf("\n");
                last = i;
                break;
            default:
                printf("%c",i);
                last = i;
                break;
        }
    }
    return 0;
}
3
  • Use ctype.h isspace() instead of isWhiteSpace(int i). And this, #define AMPERSAND 38 is not needed you just nee #define AMPERSAND '&' Commented Feb 20, 2015 at 10:39
  • Don't name functions with all uppercase. By convention, that's used for macros. Commented Feb 20, 2015 at 10:44
  • If you want to replicate bash behavior, better look at bash code... But you probably want just something resembling Unix shells? Commented Feb 20, 2015 at 10:57

3 Answers 3

2

You're not setting last every time through the loop, only in certain cases of the switch. You should also use PRINT_N_NEW when printing ordinary characters, so that it will test the last character and put a newline before it. Finally, if the character you're printing is a special character, you need to print a newline before it.

There's no point in doing last = c at the end of PRINT_N_NEW. That doesn't set the variable in the caller, it just sets the local copy.

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

#define ALPHABET 1000
#define LESS_THAN 60
#define GREATER_THAN 62
#define AMPERSAND 38
#define PIPE 124
#define WHITESPACE 32
#define NEWLINE 10
#define MYCOPOUT 9999

#define CAPITALS(i) ((i >= 65) && (i <= 90))
#define LOWERS(i) ((i >= 97) && (i <= 122))
#define BACKSLASH(i) i == 47

int classify(int i)
{
    if (CAPITALS(i) || LOWERS(i) || BACKSLASH(i))
        return ALPHABET;
    return i;
}

char isSpecial(int i)
{
    return (i == LESS_THAN) ||(i == GREATER_THAN) || (i == AMPERSAND) || (i == PIPE);
}

char isWhiteSpace(int i)
{
    return (i == WHITESPACE) || (i == NEWLINE);
}

void PRINT_N_NEW(int c, int last)
{
    if (isWhiteSpace(last)) {
        printf("%c",c);
    } else if(isSpecial(last) || isSpecial(c)) {
        printf("\n%c",c);
    } else {
        printf("%c", c);
    }
}

int main(int argc, char** argv)
{

    int i;
    int last = MYCOPOUT;
    while ( (i = getchar()) != EOF){
        switch(classify(i)){
            case ALPHABET:
                PRINT_N_NEW(i, last);
                break;
            case LESS_THAN:
                PRINT_N_NEW(i,last);
                break;
            case GREATER_THAN:
                PRINT_N_NEW(i,last);
                break;
            case AMPERSAND:
                PRINT_N_NEW(i,last);
                break;
            case PIPE:
                PRINT_N_NEW(i,last);
                break;
            case WHITESPACE:
                printf("\n");
                break;
            case NEWLINE:
                printf("\n");
                break;
            default:
                PRINT_N_NEW(i, last);
                break;
        }
        last = i;
    }
    return 0;
}
Sign up to request clarification or add additional context in comments.

Comments

0

You code is working fine,

if (isWhiteSpace(last)) {
    printf("%c\n",c);

Place the new line in this print statement. Or else place the new line after when pipe is came.

1 Comment

This works somewhat.. however for some reason, if I do echo hello| world it now results in echo, hello|, world (each comma is a new line)
0

May you should use strtok?

http://www.tutorialspoint.com/c_standard_library/c_function_strtok.htm

try to separate substrings with " " symbol, it might be useful

To solve the problem, you could check if the first char of the token is a symbol

if ( token[0] == "|" ) aux_str = token+1;

It could be like:

enter code here
#define (CMD_SEPARATORS) ("|<>&")


int function interpretCMD(char *s){
    char **args = malloc (sizeof(char*) * MAX_ARGS);


    cmd = strtok (s, " ");

    args = strtok(s);
    while (cmd != NULL){
        args[i] = strtok(s);
    }
/*
    at this point you must have cmd the cmd
    and in args[i] the i argument
*/
}

int function parseCMD(char *s){
    cmd = strtok(s, CMD_SEPARATORS);
    while (cmd!= NULL){
        interpretCMD(token);
        cmd = strtok(s);
    }
}

main blablah

while ( (line = getline()) != EOF){
    parseCMD(line);


}

6 Comments

What does this have to do with where he prints newlines while scanning?
well, the strtok function allows you to get every word in the line readed from input. So you'll be able to interpret as bash like: cmd = first_token, every next token are arguments unless token[0] its in ("|",";","&",">", "<") -> manage that options with a switch to redirect input or launch two commands at time, etc.
What if he writes cat foo|wc -l. Checking just token[0] won't find that.
uuuhmm you're right, well, strtok allow to make the separation on one or more symbols... may be.... strtok (line, " |;&><"); i'm thinking on that please wait
uhum what if you make a function like ` for (i=0; i<strlen(token);i++) if (isSymbol(token[i])) then blablah()`
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.