Własny shell - pomoc

0

Witam, muszę napisać własnego shella w C.

Chciałbym, żeby ktoś znający się na tym zweryfikował czy to co zrobiłem do tej pory ma sens.
Proszę o nie wklejanie kodów z poprawionymi błędami, tylko o wskazanie błędy z jakąś podpowiedzią. :)
Nie mam pojęcia jak napisać cd, a jako dodatkowe polecenia chciałem dodać cp i rm.
Czas na napisanie tego mam do końca tygodnia :/

Program ten powinien przyjmować na wejściu polecenia,
a następnie wykonywać działania zgodne z ich treścią. Powłoka taka powinna:
-wyświetlać znak zachęty w postaci [{path}], gdzie {path} jest ścieżką do bieżącego katalogu roboczego, ˙
-obsługiwać polecenie powłoki cd, działające analogicznie do tego znanego
nam z powłoki bash,
-obsługiwać polecenie powłoki exit, kończące działanie programu powłoki. Polecenie exit powinno przyjmować jeden opcjonalny parametr,
będący statusem wyjścia zwracanym do procesu rodzica,
-obsługiwać polecenie powłoki help, wyświetlające na ekranie informacje o autorze programu i oferowanych przez niego funkcjonalnościach,
-obsługiwać dwa inne, dowolnie wybrane polecenie powłoki,
-przyjmować polecenia odwołujące się przez nazwę do skryptów i programów znajdujących się w katalogach opisanych wartością zmiennej
środowiskowej PATH oraz umożliwiać wywołanie tych skryptów i programów z argumentami,
-przyjmować polecenia odwołujące się przez ścieżki względne oraz bezwzględne do skryptów i programów znajdujących się na dysku komputera,
a także umożliwiać ich wywołanie z argumentami,
-wypisywać komunikat błędu, gdy niemożliwe jest poprawne zinterpretowanie polecenia.

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

int main()
{
    while(1){

	//Znak zachęty

	char path[PATH_MAX];
    getcwd(path, sizeof(path));
    printf("%s~$ ", path);

	////////////////////////////////

    char polecenie[150];
    int isHelp, isCd, isExit, isExit1, isFread;

    scanf("%15s", polecenie);

    isExit = strcmp(polecenie, "exit");
    isExit1 = strcmp(polecenie, "exit-u");
    isCd = strcmp(polecenie, "cd");
    isHelp = strcmp(polecenie, "help");

    if(isExit==0){
    exit(0);
    }

    if(isExit1==0){
    exit(1);
    }

    else if(isCd==0){
    printf("cd\n");
    }

    else if(isHelp==0){
    printf("Shell\n");
    printf("Autor: \n");
    printf("\n");
    printf("Polecenia:\n");
    printf("help - pomoc\n");
    printf("exit - wyjscie\n");
    printf("exit-u - wyjscie zakonczonie niepowodzeniem\n");
    }

    else{
        printf("Bledne polecenie!\n");
    }
    }
    return 0;
}

1

https://www.systutorials.com/docs/linux/man/2-chdir/
Swoją drogą, jeśli chcesz to zrobić dobrze, rozważyłbym użyć flexa i bisona. Parsowanie w czystym C to mało przyjemna rzecz.

1

Kilka słów uwag:

  1. tu jest zaledwie próba wstępu do zarysu wykrywania komend, nie ma żadnej nawet próby wykonania. Jesteśmy w lesie.
  2. logika kodu jest jako ogromny if then else - fatalna
pomyśl o czymś takim
struct Command {
const char * cmdString;
void * execute(); // wskażnik funkcjyny
const char * [] // ewentualnie coś o dopuszczalny parametrach. Z tego główny parser dowie się o możliwej ilości, bopierze stringi do help'a itd
};

struct Command[] list ;

Każda komenda będzie jedną funkcją, będą niezależne, i nie będzie musiała zajmować się parsowaniem, tylko wykonaniem
poszerzalność, konserowalność każdej z komend znacznie wzrośnie

0
elwis napisał(a):

https://www.systutorials.com/docs/linux/man/2-chdir/
Swoją drogą, jeśli chcesz to zrobić dobrze, rozważyłbym użyć flexa i bisona. Parsowanie w czystym C to mało przyjemna rzecz.

Dzięki za link :) , musi być czyste C

0
AnyKtokolwiek napisał(a):

Kilka słów uwag:

  1. tu jest zaledwie próba wstępu do zarysu wykrywania komend, nie ma żadnej nawet próby wykonania. Jesteśmy w lesie.
  2. logika kodu jest jako ogromny if then else - fatalna
pomyśl o czymś takim
struct Command {
const char * cmdString;
void * execute(); // wskażnik funkcjyny
const char * [] // ewentualnie coś o dopuszczalny parametrach. Z tego główny parser dowie się o możliwej ilości, bopierze stringi do help'a itd
};

struct Command[] list ;

Każda komenda będzie jedną funkcją, będą niezależne, i nie będzie musiała zajmować się parsowaniem, tylko wykonaniem
poszerzalność, konserowalność każdej z komend znacznie wzrośnie

Dzięki, spróbuję jutro poprawić kod.

0

Zrobiłem coś takiego. @AnyKtokolwiek zostałem przy if'ach, ponieważ nie mogłem tego ogarnąć, a czas nagli.
Chciałbym, żeby komendy działały jak w linuxie, ale nie mogę ogarnąć jak pobrać komendy z argumentami po spacji.

Teraz planuję rozszerzenie cd (cd, cd .. itp.)
Czy dobrze zinterpretowałem? "obsługiwać polecenie powłoki exit, kończące działanie programu powłoki. Polecenie exit powinno przyjmować jeden opcjonalny parametr, będący statusem wyjścia zwracanym do procesu rodzica"
Z tego co rozumiem w "przyjmować polecenia odwołujące się przez nazwę do skryptów i programów znajdujących się w katalogach opisanych wartością zmiennej
środowiskowej PATH oraz umożliwiać wywołanie tych skryptów i programów z argumentami" i w "przyjmować polecenia odwołujące się przez ścieżki względne oraz bezwzględne do skryptów i programów znajdujących się na dysku komputera, a także umożliwiać ich wywołanie z argumentami" chodzi o to, żeby funkcje komend były w osobnych plikach, a w main były tylko odwołania do lokalizacji?

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

int main()
{
    while(1){

	//Znak zachety

	char path[PATH_MAX];
    getcwd(path, sizeof(path));
    printf("%s~$ ", path);

	////////////////////////////////

    char polecenie[150];
    int isHelp, isCd, isCd1, isExit, isExit1, isFread;

    scanf("%150s", polecenie);

    isExit = strcmp(polecenie, "exit");
    isExit1 = strcmp(polecenie, "exit-u");
    isCd = strcmp(polecenie, "cd");
    isCd1 = strcmp(polecenie, "cd-");
    isHelp = strcmp(polecenie, "help");

    if(isExit==0){
    exit(0);
    }

    else if(isExit1==0){
    exit(1);
    }

    else if(isCd==0 || isCd1==0){
    printf("Podaj lokalizacje: ");
    char cd_path[PATH_MAX];
    scanf("%s",cd_path);
    if(!strncmp(path,cd_path, sizeof cd_path)){
    chdir(cd_path);
    } else{
    char* strcat(char* path, const char* cd_path);
    chdir(cd_path);
    }
    }

    else if(isHelp==0){
    printf("Shell\n");
    printf("Autor: \n");
    printf("\n");
    printf("Polecenia:\n");
    printf("help - pomoc\n");
    printf("exit - wyjscie\n");
    printf("exit-u - wyjscie zakonczonie niepowodzeniem\n");
    printf("cd - zmiana katalogu\n");
    }

    else{
        printf("Bledne polecenie!\n");
    }
    }
    return 0;
}

0

Z tego co znalazłem w necie wynika, że nie można użyć system("exit"), a zamiast tego jest exit(int kod błędu)

0

Mam coś takiego, ale prev_path wypisuje jakieś dziwne ciągi. Kiedy wypisuję current_path i prev_path to wszystko jest ok, ale później nie działa :/

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

int main()
{
    char path_home[PATH_MAX];
    char path[PATH_MAX];
    getcwd(path_home, sizeof(path_home));
    char prev_path[PATH_MAX];
    char current_path[PATH_MAX];
    char cd_path[PATH_MAX];
    while(1){

	//Znak zachety

    getcwd(path, sizeof(path));
    printf("%s~$ ", path);
	////////////////////////////////
    getcwd(current_path, sizeof(current_path));

    char polecenie[150];
    int isHelp, isCd, isExit, isExit1, isFread;

    scanf("%150s", polecenie);

    isExit = strcmp(polecenie, "exit");
    isExit1 = strcmp(polecenie, "exit-u");
    isCd = strcmp(polecenie, "cd");
    isHelp = strcmp(polecenie, "help");

    if(isExit==0){
    exit(0);
    }

    else if(isExit1==0){
    exit(1);
    }

    else if(isCd==0){
    printf("Podaj lokalizacje: ");
    scanf("%s",cd_path);
    if(strncmp(current_path,cd_path, sizeof cd_path)!=0)
    {
        char* strcpy(char* prev_path, const char* prev_current);
        printf("%s\n",prev_path);
    }
    if(strncmp("cd-",cd_path,3)==0)
    {
        chdir(prev_path);
    }
    else if(strncmp("cd",cd_path,2)==0)
    {
        chdir(path_home);
    }
    else if(!strncmp(path,cd_path, sizeof cd_path)){
    chdir(cd_path);
    } else{
    char* strcat(char* path, const char* cd_path);
    chdir(cd_path);
    }
    }

    else if(isHelp==0){
    printf("Shell\n");
    printf("Autor:\n");
    printf("\n");
    printf("Polecenia:\n");
    printf("help - pomoc\n");
    printf("exit - wyjscie\n");
    printf("exit-u - wyjscie zakonczonie niepowodzeniem\n");
    printf("cd - zmiana katalogu\n");
    }

    else{
        printf("Bledne polecenie!\n");
    }
    }
    return 0;
}

0

Czym jest i po co jest każda z tych zmiennych?

    char path_home[PATH_MAX];
    char path[PATH_MAX];
    char prev_path[PATH_MAX];
    char current_path[PATH_MAX];
    char cd_path[PATH_MAX];

Podziel ten program na jakieś funkcje.

0

char path_home[PATH_MAX]; - ścieżka do katalogu głównego;
char path[PATH_MAX]; - aktualna lokalizacja (do wyświetlania znaku zachęty)
char prev_path[PATH_MAX]; - poprzednia lokalizacja ( do cd- )
char current_path[PATH_MAX];- aktualna lokalizacja ( do cd- )
char cd_path[PATH_MAX]; - tablica do wpisania lokalizacji lub cd, cd- (scanf("%s",cd_path);)

Nie podzieliłem kodu, ponieważ wtedy coś się sypie przy cd. :(
Zamiast tego usunąłem kilka zmiennych, które były bezsensowne.
Jak mogę przekopiować wartość current_path do prev_path?

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

int main()
{
    char path_home[PATH_MAX];
    char path[PATH_MAX];
    getcwd(path_home, sizeof(path_home));
    char prev_path[PATH_MAX];
    char current_path[PATH_MAX];
    char cd_path[PATH_MAX];
    while(1){

	//Znak zachety

    getcwd(path, sizeof(path));
    printf("%s~$ ", path);
	////////////////////////////////
    getcwd(current_path, sizeof(current_path));

    char polecenie[150];
    scanf("%150s", polecenie);

    if((strcmp(polecenie, "exit"))==0){
    exit(0);
    }

    else if((strcmp(polecenie, "exit-u"))==0){
    exit(1);
    }

    else if((strcmp(polecenie, "cd"))==0){
    printf("Podaj lokalizacje: ");
    scanf("%s",cd_path);
    if(strncmp(current_path,cd_path, sizeof cd_path)!=0)
    {
        char* strcpy(char* prev_path, const char* prev_current);
        printf("%s\n",prev_path);
    }
    if(strncmp("cd-",cd_path,3)==0)
    {
        chdir(prev_path);
    }
    else if(strncmp("cd",cd_path,2)==0)
    {
        chdir(path_home);
    }
    else if(!strncmp(path,cd_path, sizeof cd_path)){
    chdir(cd_path);
    } else{
    char* strcat(char* path, const char* cd_path);
    chdir(cd_path);
    }
    }

    else if((strcmp(polecenie, "help"))==0){
    printf("Shell\n");
    printf("Autor: Jakub Wieczorek\n");
    printf("\n");
    printf("Polecenia:\n");
    printf("help - pomoc\n");
    printf("exit - wyjscie\n");
    printf("exit-u - wyjscie zakonczonie niepowodzeniem\n");
    printf("cd - zmiana katalogu\n");
    }

    else{
        printf("Bledne polecenie!\n");
    }
    }
    return 0;
}

0
Linux_jest_zly napisał(a):

Jak mogę przekopiować wartość current_path do prev_path?

strcpy(prev_path, current_path);
0

Aktualnie mój kod wygląda tak:

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

int main()
{
    char path_home[PATH_MAX];
    char path[PATH_MAX];
    getcwd(path_home, sizeof(path_home));
    char prev_path[PATH_MAX];
    char current_path[PATH_MAX];
    char cd_path[PATH_MAX];

    while(1){

        //Znak zachety

        getcwd(path, sizeof(path));
        printf("%s~$ ", path);

        //Pobieranie aktualnej lokalizacji

        getcwd(current_path, sizeof(current_path));

        //Pobieranie polecenia

        char polecenie[255];
        scanf(" %[^\n]",polecenie);

        //Wyjście

        if((strncmp(polecenie, "exit",4))==0)
        {

            if(strncmp(polecenie,"exit ",5)==0)
            {
                if((strcmp(strstr(polecenie,"-"),"-u"))==0)
                {
                    exit(1);
                }
                else
                {
                 printf("Ta funkcja nie przyjmuje takiego argumentu!/n");
                }
            }

            else
            {
                exit(0);
            }
        }

        //Zmiana lokalizacji

        else if((strncmp(polecenie, "cd",2))==0){

           //Zmiana lokalizacji na katalog główny

            if(strcmp(polecenie,"cd")==0)
            {
                chdir(path_home);
            }

            //Powrót do poprzedniego katalogu

            else if(strncmp("cd -",polecenie,4)==0)
            {
                if (chdir(prev_path)!= 0)
                {
                   perror("Error");
                }

                chdir(prev_path);
            }

            else{

                int j = sizeof polecenie;

                for(int i=0;i<=j-3;i++)
                {
                    polecenie[i]=polecenie[i+3];
                }

                //Zmiana lokalizacji poprzez wpisanie ścieżki bezwzględnej

                if(strncmp(polecenie,"/",1)==0)
                {
                    if (chdir(polecenie)!=0)
                    {
                       perror("Error");
                    }

                    chdir(polecenie);
                }

                //Zmiana lokalizacji poprzez wpisanie ścieżki względnej

                else
                {
                    char* strcat(char* path, const char* polecenie);

                    if (chdir(polecenie)!=0)
                    {
                       perror("Error");
                    }

                    chdir(polecenie);

                }
            }

            //Przypisywanie do zmiennej prev_path poprzedniej lokalizacji

            if(strncmp(current_path,cd_path, sizeof current_path)!=0)
            {
                strcpy(prev_path, current_path);
            }
        }


        //Tworzenie pliku

        else if((strncmp(polecenie,"touch",5))==0)
        {

            int j = sizeof polecenie;

            for(int i=0;i<=j-6;i++)
            {
                polecenie[i]=polecenie[i+6];
            }

            FILE* file;
            file=fopen(polecenie,"w");
            fclose(file);
        }

        //Usuwanie pliku

        else if((strncmp(polecenie,"rm",2))==0)
        {

            int j = sizeof polecenie;

            for(int i=0;i<=j-3;i++)
            {
                polecenie[i]=polecenie[i+3];
            }

            if (remove(polecenie)!= 0)
            {
                perror("Error");
            }

            remove(polecenie);
        }

        //Wyświetlanie pomocy

        else if((strcmp(polecenie, "help"))==0)
        {
            printf("####################################\n\n");
            printf("Shell\n");
            printf("\n");
            printf("Autor: \n");
            printf("\n");
            printf("Polecenia:\n");
            printf("help - pomoc\n");
            printf("exit - wyjscie\n");
            printf("exit -u - wyjscie zakonczonie niepowodzeniem\n");
			printf("touch - tworzenie pliku\n");
			printf("rm - usuwanie pliku\n");
			printf("echo - wypisuje ciag znakow podany jako argument\n");
			printf("pwd - wypisuje aktualna lokalizacje uzytkownika\n");
            printf("cd - zmiana katalogu\n");
            printf("Dostepne argumenty polecenia cd:\n");
            printf("cd - - powrot do poprzedniego katalogu\n");
            printf("cd .. - przejscie do katalogu nadrzednego\n");
            printf("cd - przejscie katalogu glownego\n");
            printf("Mozna podac takze lokalizacje wzgledna lub bezwzgledna\n\n");
            printf("Przy tworzeniu kodu kozystałem z dokumentacji dostepnej online\noraz z pomocy na forum 4programmers.net\nOdnosnik do watku, ktory zalozylem: https://4programmers.net/Forum/C_i_C++/320380-wlasny_shell_pomoc\nKod jest w 100%% mojego autorstwa\n\n");
            printf("####################################\n\n");
        }

        else if((strncmp(polecenie, "echo",4))==0)
        {
            int j = sizeof polecenie;

            for(int i=0;i<=j-5;i++)
            {
                polecenie[i]=polecenie[i+5];
            }

            printf("%s\n",polecenie);
        }

        else if((strncmp(polecenie, "pwd",3))==0)
        {
            printf("Twoja aktualna lokalizacja to: %s\n",current_path);
        }

        else
        {
            printf("Bledne polecenie!\n");
        }
    }

    return 0;
}

Do tego mam plik Makefile:

all:
	g++ shell.c -o shell
install: all
	install -d /shell
clean:
	rm -r *.o shell

Czy ktoś mógłby mi powiedzieć ile dostanę, za to pkt i co musiałbym poprawić?
https://bap.faculty.wmi.amu.edu.pl/wp-content/uploads/2018/11/zadanie-shell.pdf

0
/tmp $ gcc -std=c11 -g -Werror -Wall -pedantic -fsanitize=shift -fsanitize=integer-divide-by-zero -fsanitize=unreachable -fsanitize=vla-bound -fsanitize=null -fsanitize=return -fsanitize=signed-integer-overflow -Wextra shell.c
shell.c: In function ‘main’:
shell.c:9:20: error: ‘PATH_MAX’ undeclared (first use in this function); did you mean ‘RAND_MAX’?
     char path_home[PATH_MAX];
                    ^~~~~~~~
                    RAND_MAX
shell.c:9:20: note: each undeclared identifier is reported only once for each function it appears in
shell.c:14:10: error: unused variable ‘cd_path’ [-Werror=unused-variable]
     char cd_path[PATH_MAX];
          ^~~~~~~
shell.c:13:10: error: unused variable ‘current_path’ [-Werror=unused-variable]
     char current_path[PATH_MAX];
          ^~~~~~~~~~~~
shell.c:12:10: error: unused variable ‘prev_path’ [-Werror=unused-variable]
     char prev_path[PATH_MAX];
          ^~~~~~~~~
shell.c:10:10: error: unused variable ‘path’ [-Werror=unused-variable]
     char path[PATH_MAX];
          ^~~~
shell.c:9:10: error: unused variable ‘path_home’ [-Werror=unused-variable]
     char path_home[PATH_MAX];
          ^~~~~~~~~
cc1: all warnings being treated as errors

A jeśli być zgodnym z poleceniem (i pisać w ANSI C), to błędów jest więcej:

shell.c: In function ‘main’:
shell.c:9:20: error: ‘PATH_MAX’ undeclared (first use in this function); did you mean ‘RAND_MAX’?
     char path_home[PATH_MAX];
                    ^~~~~~~~
                    RAND_MAX
shell.c:9:20: note: each undeclared identifier is reported only once for each function it appears in
shell.c:12:5: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
     char prev_path[PATH_MAX];
     ^~~~
shell.c:18:9: error: C++ style comments are not allowed in ISO C90
         //Znak zachety
         ^
shell.c:18:9: error: (this will be reported only once per input file)
shell.c:29:9: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
         char polecenie[255];
         ^~~~
shell.c:82:17: error: ‘for’ loop initial declarations are only allowed in C99 or C11 mode
                 for(int i=0;i<=j-3;i++)
                 ^~~
shell.c:82:17: note: use option -std=c99, -std=gnu99, -std=c11 or -std=gnu11 to compile your code
shell.c:130:13: error: ‘for’ loop initial declarations are only allowed in C99 or C11 mode
             for(int i=0;i<=j-6;i++)
             ^~~
shell.c:135:13: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
             FILE* file;
             ^~~~
shell.c:147:13: error: ‘for’ loop initial declarations are only allowed in C99 or C11 mode
             for(int i=0;i<=j-3;i++)
             ^~~
shell.c:191:13: error: ‘for’ loop initial declarations are only allowed in C99 or C11 mode
             for(int i=0;i<=j-5;i++)
             ^~~
shell.c:14:10: error: unused variable ‘cd_path’ [-Werror=unused-variable]
     char cd_path[PATH_MAX];
          ^~~~~~~
shell.c:13:10: error: unused variable ‘current_path’ [-Werror=unused-variable]
     char current_path[PATH_MAX];
          ^~~~~~~~~~~~
shell.c:12:10: error: unused variable ‘prev_path’ [-Werror=unused-variable]
     char prev_path[PATH_MAX];
          ^~~~~~~~~
shell.c:10:10: error: unused variable ‘path’ [-Werror=unused-variable]
     char path[PATH_MAX];
          ^~~~
shell.c:9:10: error: unused variable ‘path_home’ [-Werror=unused-variable]
     char path_home[PATH_MAX];
          ^~~~~~~~~
cc1: all warnings being treated as errors
0
Linux_jest_zly napisał(a):

Czy ktoś mógłby mi powiedzieć ile dostanę, za to pkt i co musiałbym poprawić?
https://bap.faculty.wmi.amu.edu.pl/wp-content/uploads/2018/11/zadanie-shell.pdf

Podziel to na funkcje bo aktualnie nie nadaje się do publikacji.

Przykładowa lista:

  • display_help
  • parse_args
  • get_command
  • exec_command
  • exec_cd
  • exec_rm
    ...
0

exit nie działa zgodnie z opisem:

/tmp $ ./custom_shell 
/tmp~$ exit
/tmp $ ./custom_shell 
/tmp~$ exit 0
fish: “./custom_shell” terminated by signal SIGSEGV (Address boundary error)
/tmp $ ./custom_shell 
/tmp~$ exit 1
fish: “./custom_shell” terminated by signal SIGSEGV (Address boundary error)

Nie widzę także wcale możliwości wykonywania własnych poleceń (a co dopiero podawać im argumentów).

0

@enedil:
Nie umiem zrobić wykonywania własnych poleceń :(
Dzięki za zmienne (zapomniałem usunąć) i exit (myślałem, że sam sobie mam wybrać parametr)
Już zmieniłem exit - może przyjąć każdą liczbę jako parametr
Dziękuję wszystkim za pomoc mam nadzieję, że uda się zdać :)

0

W kwestii własnych poleceń, zapoznaj się z rodzinną funkcji execv:

man 3 execv
0

Zdaje się, że Twoje polecenie touch usuwa zawartość istniejących plików. Nie powinno tego robić.

if (chdir(prev_path)!= 0)
{
  perror("Error");
}

chdir(prev_path);

To drugie chdir() niepotrzebne (już pierwsze w warunku się wykonało).

getcwd(path, sizeof(path));
printf("%s~$ ", path);
...
getcwd(current_path, sizeof(current_path));

Zmienna path nie jest później używana? To usuń ją i użyj current_path w tym miejscu (zawiera to samo):

getcwd(current_path, sizeof(current_path));
printf("%s~$ ", current_path);
1

Uam pozdrawia, Wydział matematyki i informatyki na propsie haha, ja tez do piatku musze to zrobic eh

1 użytkowników online, w tym zalogowanych: 0, gości: 1