Kommando Interpreter C

Joe1903

Mitglied
Hallo zusammen,

ich muss bei einer Programmieraufgabe einen Kommando-Interpreter in C schreiben,welches folgendes machen soll:


Kommando Interpreter mit einem Array von Kommandobeschreibungen;


eine Kommandobeschreibung enthält

- den Kommandonamen

- die Namen der Parameter

- den Pointer auf eine Funktion, die u.a. mit

- int argc /* Anzahl der Parameter in 'argv' */

- char * argv[] /* Parameter (als Strings) */

aufgerufen wird;


Wie kann ich das angehen?Kann mir jemand eine Hilfestellung geben,wie ich diese Aufgabe angehen kann,ob es evtl. bereits "Rahmenprogramme" gibt,bei denen man nur Anpassungen vornehmen muss etc.

Vielen Dank im Voraus.
 
Kommt dir int argc, char* argv[] bekannt vor?
In diesem Format ist auch die int main().

Es funktioniert also so:
Ein erstes scanf("%s") liest den Kommandonamen. Dann weisst du schon, ob das Kommando existiert. Wenn nicht: Abbruch, Leeren des input buffers (gets()) und beginne von vorne.
Ansonsten: Scanf, bis alle Parameter gelesen sind. Diese Parameter sollten in einem char* arr[] gespeichert werden, die Anzahl in einem uint. Dann einfach die verlinkte Funktion mit den Parametern aufrufen -> fertig.

Gruss
cwriter
 
Kommt dir int argc, char* argv[] bekannt vor?
In diesem Format ist auch die int main().

Es funktioniert also so:
Ein erstes scanf("%s") liest den Kommandonamen. Dann weisst du schon, ob das Kommando existiert. Wenn nicht: Abbruch, Leeren des input buffers (gets()) und beginne von vorne.
Ansonsten: Scanf, bis alle Parameter gelesen sind. Diese Parameter sollten in einem char* arr[] gespeichert werden, die Anzahl in einem uint. Dann einfach die verlinkte Funktion mit den Parametern aufrufen -> fertig.

Gruss
cwriter

Glaubst du,du könntest mir auch hier den Rohbau aufzeigen?Also später.
 
Glaubst du,du könntest mir auch hier den Rohbau aufzeigen?Also später.
Ich glaube nicht, ich weiss es.
Was ich nicht weiss, ist, ob der Code korrekt ist. Er scheint zumindest halbwegs zu funktionieren.
Probiere einfach mal den Input "adder 0 1 2 3 4 <" (das '<' beendet die Argumentkette) durch.
Ich habe etwas viel mit realloc herumgespielt, um dir die Mechaniken ein bisschen zu zeigen - aber es ist gut möglich, dass ich da selbst auf die Schnauze gefallen bin...

Und da VS prinzipiell ungeprüfte Funktionen nicht so mag, sind manche Funktionen im Code non-Standard. Ich habe ein paar defines hingeklatscht, die helfen sollten.

Das ist alles so in einer knappen Stunde entstanden, also kein (grosser) Gewähr :)

C++:
//VS-Specific, ignore
#define _CRT_SECURE_NO_WARNINGS

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

//Function signature definition
typedef int (*cmd_func)(int argc, char* argv[]);

//Some hacky defines
#define strcpy_s(dest, s, src) strcpy(dest, src);
#define memcpy_s(dest, destsize, source, sourcesize) memcpy(dest, source, sourcesize);


typedef struct {
    char* name;            //Command name; will be checked against
    int parc;            //param count: How many strings there are in parv
    char** parv;        //Names of the parameters (for whatever reason we have them here...)
    int argc;            //argument count: Value to be passed to func
    char** argv;        //argument vector: Value to be passed to func
    cmd_func func;        //Actual command function

} cmd_desc;

///<summary>
///Extended free: Frees only if p != NULL
///</summary>
///<returns>true if p was freed, false otherwise</returns>
bool exfree(void* p)
{
    if (p != NULL) {
        free(p);
        return true;
    }
    return false;
}

///<summary>
///Clears a cmd_desc struct instance by setting all bytes to 0
///</summary>
///<param name="to_clear">pointer to allocated structure that shall be cleared</param>
void clear_cmd_desc(cmd_desc* to_clear)
{
    exfree(to_clear->name);
    for (int i = 0; i < to_clear->parc; i++) exfree(to_clear->parv[i]);    //Free parameter names
    for (int i = 0; i < to_clear->argc; i++) exfree(to_clear->argv[i]);    //Free arguments
    memset(to_clear, 0, sizeof(cmd_desc));
}

///<summary>
///Creates a valid cmd_desc structure with the values provided as parameters.
///</summary>
///<returns>A calloc-created pointer to a valid struct, or NULL if no valid struct could be generated</returns>
cmd_desc* create_cmd_desc(const char* name, int parc, const char** parv, cmd_func func)
{
    if (parc < 0) return NULL;
    cmd_desc* ptr = (cmd_desc*)calloc(1, sizeof(cmd_desc));    //calloc is very nice here: it automatically sets the memory to 0
    if (ptr == NULL) return NULL;
    ptr->name = (char*)malloc(strlen(name) + 1);
    strcpy_s(ptr->name, strlen(name) + 1, name);        //This is non-standard. If errors occur: Remove the 2nd param and change to strcpy().

    //Some (illegal) shortcut.
    if (parc > 0)
    {
        ptr->parc = parc;

        ptr->parv = (char**)malloc(sizeof(char*)*parc);
        //Loop over all arguments to copy, then allocate, then copy
        for (int i = 0; i < parc; i++) {
            ptr->parv[i] = (char*)malloc(strlen(parv[i]) + 1);
            strcpy_s(ptr->parv[i], strlen(parv[i]) + 1, parv[i]);    //Non-standard again
        }
    }
    ptr->func = func;

    return ptr;


}

///<summary>
///Pushes an input at the end of a cmd_desc structure
///</summary>
bool push_input(cmd_desc* d, const char* input)
{
    char** tmp = (char**)realloc(d->argv, sizeof(char*) * (d->argc + 1));    //WARNING: This is really bad. If you fancy, try making this more efficient.
    //One way of making the line above more efficient: Double the current size each time we would exceed the current memory limit. This would logarithmically decrease the need of new allocations.
    if (tmp == NULL) return false;
    else d->argv = tmp;
    d->argv[d->argc] = (char*)malloc(strlen(input) + 1);
    strcpy_s(d->argv[d->argc], strlen(input) + 1, input);
    d->argc++;
    return true;
}

///<summary>
///Clears all input from cmd_desc* d.
///</summary>
void clear_input(cmd_desc* d)
{
    for (int i = 0; i < d->argc; i++) exfree(d->argv[i]);
    exfree(d->argv);
    d->argv = NULL;
    d->argc = 0;
}

///<summary>
///Adds a cmd_desc to the global array
///</summary>
bool add_cmd_desc(cmd_desc** arr, size_t* index, size_t* arrsize, cmd_desc* desc)
{
    //Logarithmic reallocation procedure
    if (*index >= *arrsize)
    {
        cmd_desc* tmp = (cmd_desc*)realloc(*arr, sizeof(cmd_desc) * *arrsize * 2);
        if (tmp == NULL) return false;
        else {
            *arrsize *= 2;
            *arr = tmp;
        }
    }
    //Set copy the contents of desc (*desc) to its location in the array
    (*arr)[(*index)++] = *desc;

    //Free desc as we have copied it already
    free(desc);
    return true;
}

///<summary>
///Converts all strings in argv to integers and adds them, returning the sum. It prints sum, average and median to stdout.
///</summary>
///<returns>Integer sum of all valid values in argv</returns>
int adder(int argc, char* argv[])
{
    if (argc <= 0)
    {
        printf("Invalid parameters\n");
        return 0;
    }
    int sum = 0;
    for (int i = 0; i < argc; i++)
    {
        sum += atoi(argv[i]);
    }
    printf("Sum is %d, Average is %d and median is %d\n", sum, sum / argc, atoi(argv[argc / 2]));
    return sum;
}

int main(int argc, char* argv[])
{
    cmd_desc* cmd_desc_arr = NULL;
    size_t arr_ind = 0;
    size_t arr_size = 4;

    cmd_desc_arr = (cmd_desc*)calloc(arr_size, sizeof(cmd_desc));
    if (cmd_desc_arr == NULL) return -1;

    size_t bufstep = 256;
    size_t bufsize = 256;
    char* buf = (char*)calloc(bufsize, sizeof(char));    //Input buffer
    char* bufbase = buf;

    if (buf == NULL)
    {
        free(cmd_desc_arr);    //Free some sad things
        return -1;
    }

    //Add our little example function. We shortcut the descriptions 'cause I have no clue what they are for anyways.
    cmd_desc* d = create_cmd_desc("adder", 0, NULL, &adder);
    if (d == NULL) printf("Uh. Something really bad happened. And the programmer was too lazy to clean up... Take cover.\n");
    add_cmd_desc(&cmd_desc_arr, &arr_ind, &arr_size, d);

    printf("This is a little command line interface (cli) demo.\nEnter 'quit' to quit.\n");



    while (scanf("%255s", bufbase) > 0) //Scanf string, 255 characters at max.
    {
        if (strcmp(bufbase, "quit") == 0) break;
        for (size_t i = 0; i < arr_ind; i++)
        {
          
            cmd_desc* d = &cmd_desc_arr[i];
          
            if (d->name == NULL) continue;

            if (strcmp(d->name, buf) == 0)
            {
                //Function found, now we can read data in
                while (scanf("%255s", buf) > 0) //This format string could be created with ssprintf, but I'm too lazy now
                {
                    if (buf[0] == '<') break;
                    //If not full yet / Broken mid-word, so we need to fix it...
                    if (buf[bufstep-2] != 0)
                    {
                        buf += bufstep-1;
                    }

                    if (bufbase[bufsize - 2] != 0) {
                        //No more space here, so we have to increase the size
                        char* tmp = (char*)realloc(bufbase, bufsize * 2);
                        if (tmp == NULL) {
                            printf("Failed to realloc.\n");
                            goto end;
                        }
                        int diff = (buf - bufbase);
                        bufbase = tmp;
                        //Some magic here: Reset the buf to keep its relative offset to bufbase
                        buf = buf + diff;

                        bufsize *= 2;

                        //Some safety measures
                        bufbase[bufsize - 2] = 0;
                        bufbase[bufsize - 1] = 0;
                    }
                    //Else we are done here and we can add the argument

                    push_input(d, bufbase);
                }

                //Call the functions
                d->func(d->argc, d->argv);

                //Clean up
                clear_input(d);

                //We could cleanup, i.e. reduce the size of the input buffer here. I won't.
                break;    //Break as there should only be one function per name
            }
        }

      

        printf("Ladies and gentlemen, next round, your input please...\n");
    }
end:

    free(bufbase);

    for (size_t i = 0; i < arr_size; i++)
    {
        clear_cmd_desc(&cmd_desc_arr[i]);
    }
    exfree(cmd_desc_arr);

    getchar();
    return 0;
}

Ich habe dir auch die .cpp angehängt, falls du nicht gerne aus dem Forum kopierst.

Gruss
cwriter

/EDIT: Der Median in der Beispielfunktion ist natürlich nur der Median, falls die Zahlenfolge sortiert ist... Sonst ist es einfach die mittlere Zahl der Zahlenfolge.
Mea Culpa.
 

Anhänge

  • main.cpp
    7,1 KB · Aufrufe: 2
Zuletzt bearbeitet:
Ich glaube nicht, ich weiss es.
Was ich nicht weiss, ist, ob der Code korrekt ist. Er scheint zumindest halbwegs zu funktionieren.
Probiere einfach mal den Input "adder 0 1 2 3 4 <" (das '<' beendet die Argumentkette) durch.
Ich habe etwas viel mit realloc herumgespielt, um dir die Mechaniken ein bisschen zu zeigen - aber es ist gut möglich, dass ich da selbst auf die Schnauze gefallen bin...

Und da VS prinzipiell ungeprüfte Funktionen nicht so mag, sind manche Funktionen im Code non-Standard. Ich habe ein paar defines hingeklatscht, die helfen sollten.

Das ist alles so in einer knappen Stunde entstanden, also kein (grosser) Gewähr :)

C++:
//VS-Specific, ignore
#define _CRT_SECURE_NO_WARNINGS

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

//Function signature definition
typedef int (*cmd_func)(int argc, char* argv[]);

//Some hacky defines
#define strcpy_s(dest, s, src) strcpy(dest, src);
#define memcpy_s(dest, destsize, source, sourcesize) memcpy(dest, source, sourcesize);


typedef struct {
    char* name;            //Command name; will be checked against
    int parc;            //param count: How many strings there are in parv
    char** parv;        //Names of the parameters (for whatever reason we have them here...)
    int argc;            //argument count: Value to be passed to func
    char** argv;        //argument vector: Value to be passed to func
    cmd_func func;        //Actual command function

} cmd_desc;

///<summary>
///Extended free: Frees only if p != NULL
///</summary>
///<returns>true if p was freed, false otherwise</returns>
bool exfree(void* p)
{
    if (p != NULL) {
        free(p);
        return true;
    }
    return false;
}

///<summary>
///Clears a cmd_desc struct instance by setting all bytes to 0
///</summary>
///<param name="to_clear">pointer to allocated structure that shall be cleared</param>
void clear_cmd_desc(cmd_desc* to_clear)
{
    exfree(to_clear->name);
    for (int i = 0; i < to_clear->parc; i++) exfree(to_clear->parv[i]);    //Free parameter names
    for (int i = 0; i < to_clear->argc; i++) exfree(to_clear->argv[i]);    //Free arguments
    memset(to_clear, 0, sizeof(cmd_desc));
}

///<summary>
///Creates a valid cmd_desc structure with the values provided as parameters.
///</summary>
///<returns>A calloc-created pointer to a valid struct, or NULL if no valid struct could be generated</returns>
cmd_desc* create_cmd_desc(const char* name, int parc, const char** parv, cmd_func func)
{
    if (parc < 0) return NULL;
    cmd_desc* ptr = (cmd_desc*)calloc(1, sizeof(cmd_desc));    //calloc is very nice here: it automatically sets the memory to 0
    if (ptr == NULL) return NULL;
    ptr->name = (char*)malloc(strlen(name) + 1);
    strcpy_s(ptr->name, strlen(name) + 1, name);        //This is non-standard. If errors occur: Remove the 2nd param and change to strcpy().

    //Some (illegal) shortcut.
    if (parc > 0)
    {
        ptr->parc = parc;

        ptr->parv = (char**)malloc(sizeof(char*)*parc);
        //Loop over all arguments to copy, then allocate, then copy
        for (int i = 0; i < parc; i++) {
            ptr->parv[i] = (char*)malloc(strlen(parv[i]) + 1);
            strcpy_s(ptr->parv[i], strlen(parv[i]) + 1, parv[i]);    //Non-standard again
        }
    }
    ptr->func = func;

    return ptr;


}

///<summary>
///Pushes an input at the end of a cmd_desc structure
///</summary>
bool push_input(cmd_desc* d, const char* input)
{
    char** tmp = (char**)realloc(d->argv, sizeof(char*) * (d->argc + 1));    //WARNING: This is really bad. If you fancy, try making this more efficient.
    //One way of making the line above more efficient: Double the current size each time we would exceed the current memory limit. This would logarithmically decrease the need of new allocations.
    if (tmp == NULL) return false;
    else d->argv = tmp;
    d->argv[d->argc] = (char*)malloc(strlen(input) + 1);
    strcpy_s(d->argv[d->argc], strlen(input) + 1, input);
    d->argc++;
    return true;
}

///<summary>
///Clears all input from cmd_desc* d.
///</summary>
void clear_input(cmd_desc* d)
{
    for (int i = 0; i < d->argc; i++) exfree(d->argv[i]);
    exfree(d->argv);
    d->argv = NULL;
    d->argc = 0;
}

///<summary>
///Adds a cmd_desc to the global array
///</summary>
bool add_cmd_desc(cmd_desc** arr, size_t* index, size_t* arrsize, cmd_desc* desc)
{
    //Logarithmic reallocation procedure
    if (*index >= *arrsize)
    {
        cmd_desc* tmp = (cmd_desc*)realloc(*arr, sizeof(cmd_desc) * *arrsize * 2);
        if (tmp == NULL) return false;
        else {
            *arrsize *= 2;
            *arr = tmp;
        }
    }
    //Set copy the contents of desc (*desc) to its location in the array
    (*arr)[(*index)++] = *desc;

    //Free desc as we have copied it already
    free(desc);
    return true;
}

///<summary>
///Converts all strings in argv to integers and adds them, returning the sum. It prints sum, average and median to stdout.
///</summary>
///<returns>Integer sum of all valid values in argv</returns>
int adder(int argc, char* argv[])
{
    if (argc <= 0)
    {
        printf("Invalid parameters\n");
        return 0;
    }
    int sum = 0;
    for (int i = 0; i < argc; i++)
    {
        sum += atoi(argv[i]);
    }
    printf("Sum is %d, Average is %d and median is %d\n", sum, sum / argc, atoi(argv[argc / 2]));
    return sum;
}

int main(int argc, char* argv[])
{
    cmd_desc* cmd_desc_arr = NULL;
    size_t arr_ind = 0;
    size_t arr_size = 4;

    cmd_desc_arr = (cmd_desc*)calloc(arr_size, sizeof(cmd_desc));
    if (cmd_desc_arr == NULL) return -1;

    size_t bufstep = 256;
    size_t bufsize = 256;
    char* buf = (char*)calloc(bufsize, sizeof(char));    //Input buffer
    char* bufbase = buf;

    if (buf == NULL)
    {
        free(cmd_desc_arr);    //Free some sad things
        return -1;
    }

    //Add our little example function. We shortcut the descriptions 'cause I have no clue what they are for anyways.
    cmd_desc* d = create_cmd_desc("adder", 0, NULL, &adder);
    if (d == NULL) printf("Uh. Something really bad happened. And the programmer was too lazy to clean up... Take cover.\n");
    add_cmd_desc(&cmd_desc_arr, &arr_ind, &arr_size, d);

    printf("This is a little command line interface (cli) demo.\nEnter 'quit' to quit.\n");



    while (scanf("%255s", bufbase) > 0) //Scanf string, 255 characters at max.
    {
        if (strcmp(bufbase, "quit") == 0) break;
        for (size_t i = 0; i < arr_ind; i++)
        {
         
            cmd_desc* d = &cmd_desc_arr[i];
         
            if (d->name == NULL) continue;

            if (strcmp(d->name, buf) == 0)
            {
                //Function found, now we can read data in
                while (scanf("%255s", buf) > 0) //This format string could be created with ssprintf, but I'm too lazy now
                {
                    if (buf[0] == '<') break;
                    //If not full yet / Broken mid-word, so we need to fix it...
                    if (buf[bufstep-2] != 0)
                    {
                        buf += bufstep-1;
                    }

                    if (bufbase[bufsize - 2] != 0) {
                        //No more space here, so we have to increase the size
                        char* tmp = (char*)realloc(bufbase, bufsize * 2);
                        if (tmp == NULL) {
                            printf("Failed to realloc.\n");
                            goto end;
                        }
                        int diff = (buf - bufbase);
                        bufbase = tmp;
                        //Some magic here: Reset the buf to keep its relative offset to bufbase
                        buf = buf + diff;

                        bufsize *= 2;

                        //Some safety measures
                        bufbase[bufsize - 2] = 0;
                        bufbase[bufsize - 1] = 0;
                    }
                    //Else we are done here and we can add the argument

                    push_input(d, bufbase);
                }

                //Call the functions
                d->func(d->argc, d->argv);

                //Clean up
                clear_input(d);

                //We could cleanup, i.e. reduce the size of the input buffer here. I won't.
                break;    //Break as there should only be one function per name
            }
        }

     

        printf("Ladies and gentlemen, next round, your input please...\n");
    }
end:

    free(bufbase);

    for (size_t i = 0; i < arr_size; i++)
    {
        clear_cmd_desc(&cmd_desc_arr[i]);
    }
    exfree(cmd_desc_arr);

    getchar();
    return 0;
}

Ich habe dir auch die .cpp angehängt, falls du nicht gerne aus dem Forum kopierst.

Gruss
cwriter

/EDIT: Der Median in der Beispielfunktion ist natürlich nur der Median, falls die Zahlenfolge sortiert ist... Sonst ist es einfach die mittlere Zahl der Zahlenfolge.
Mea Culpa.
Vielen vielen Dank für deine Mühe.Ich habs getestet,konnte es aber nicht zum Laufen bringen.
Könntest du es überfliegen,ob dir ein Fehler ins Auge fällt?Nochmals vielen Dank.
 
Vielen vielen Dank für deine Mühe.Ich habs getestet,konnte es aber nicht zum Laufen bringen.
Könntest du es überfliegen,ob dir ein Fehler ins Auge fällt?Nochmals vielen Dank.
Und woran lag das? Ich kann keinen Bug reproduzieren, du müsstest schon ein bisschen mithelfen und deine Eingabe oder ggf. Compilerwarnungen und -fehler aufzählen.
Ich bin nicht so der Überflieger (pun intended) und kann daher nicht einfach den Fehler auf 200 Zeilen Code finden.

Gruss
cwriter
 
Und woran lag das? Ich kann keinen Bug reproduzieren, du müsstest schon ein bisschen mithelfen und deine Eingabe oder ggf. Compilerwarnungen und -fehler aufzählen.
Ich bin nicht so der Überflieger (pun intended) und kann daher nicht einfach den Fehler auf 200 Zeilen Code finden.

Gruss
cwriter
Ok ich schick morgen die Compilermeldungen.Habe grad kein Zugriff auf den Rechner und weiss die nicht mehr auswendig.
 
Und woran lag das? Ich kann keinen Bug reproduzieren, du müsstest schon ein bisschen mithelfen und deine Eingabe oder ggf. Compilerwarnungen und -fehler aufzählen.
Ich bin nicht so der Überflieger (pun intended) und kann daher nicht einfach den Fehler auf 200 Zeilen Code finden.

Gruss
cwriter
Hallo.Das sind die Fehler die ich beim Kompilieren bekommen habe:

ERROR CCN3206 ./com.c:58 Suffix of integer constant 2nd is not valid.
ERROR CCN3010 ./com.c:58 Macro strcpy invoked with a null argument for parameter x.
ERROR CCN3041 ./com.c:58 The invocation of macro strcpy contains fewer arguments than are required by the macro definition.
ERROR CCN3209 ./com.c:168 Character constants must end before the end of a line.
WARNING CCN3076 ./com.c:168 Character constant 'cause I have no clue what they are for anyways.' has more than 4 characters. No more than rightmost 4 characters are used.
ERROR CCN3209 ./com.c:190 Character constants must end before the end of a line.
WARNING CCN3076 ./com.c:190 Character constant 'm too lazy now' has more than 4 characters. No more than rightmost 4 characters are used.
ERROR CCN3209 ./com.c:228 Character constants must end before the end of a line.
INFORMATIONAL CCN4118 ./com.c:228 Character constant 't.' has more than 1 character.
 

Neue Beiträge

Zurück