c - How can I design a signal-safe shell interpreter -


the code have right sends prompt out stdout, reads line stdin. receiving sigint @ point interrupts execution , exits program. unsure should trap sigint, , know cannot start new prompt when signal received current code. there proper way accomplish (ultimate goal act shells (sigint cancels current prompt , starts new one))?

this code run on linux, less platform independent, better.

get_line reads line stdin buffer , generates char[], assigned line.
split_args takes line , transforms array of char[], splitting on whitespace.
is_command_valid determines if user typed known internal command. external programs cannot executed.

static int run_interactive(char *user) {     int done = 0;          {         char *line, **args;         int (*fn)(char *, char **);          fprintf(stderr, "gitorium (%s)> ", user);         get_line(&line);          if (line[0] == '\0')         {             free(line);             break;         }          split_args(&args, line);          if (!strcmp(args[0], "quit") || !strcmp(args[0], "exit") ||             !strcmp(args[0], "logout") || !strcmp(args[0], "bye"))             done = 1;         else if (null == args[0] ||             (!strcmp("help", args[0]) && null == args[1]))             interactive_help();         else if ((fn = is_command_valid(args)) != null)             (*fn)(user, args);         else             error("the command not exist.");          free(line);         free(args);     }     while (!done);      return 0; } 

here 2 important helper functions

static int split_args(char ***args, char *str) {     char **res = null, *p = strtok(str, " ");     int n_spaces = 0, = 0;      while (p)     {         res = realloc(res, sizeof (char*) * ++n_spaces);          if (res == null)             return gitorium_mem_alloc;          res[n_spaces-1] = p;         i++;         p = strtok(null, " ");     }      res = realloc(res, sizeof(char*) * (n_spaces+1));     if (res == null)         return gitorium_mem_alloc;      res[n_spaces] = 0;     *args = res;      return i; }  static int get_line(char **linep) {     char *line = malloc(line_buffer_size);     int len = line_buffer_size, c;     *linep = line;      if(line == null)         return gitorium_mem_alloc;      for(;;)     {         c = fgetc(stdin);         if(c == eof || c == '\n')             break;          if(--len == 0)         {             char *linen = realloc(*linep, sizeof *linep + line_buffer_size);             if(linen == null)                 return gitorium_mem_alloc;              len = line_buffer_size;             line = linen + (line - *linep);             *linep = linen;         }          *line++ = c;     }      *line = 0;     return 0; } 

if understand correctly, want know how handle signal once it.

the way establish signal handler sigaction(). didn't state platform you're on i'm assuming linux, although sigaction() defined posix standards , should available on other platforms.

there various ways can this. 1 way establish signal handler sets global variable 1, denoting signal caught. in getline() function establish check see if sigint caught , if return null , allow run_interactive() run again.

here's how catch signal:

#include <signal.h>  static int sigint_caught = 0;  static void sigint_handler(int sig) {   sigint_caught = 1; }  struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; // or sa_restart if want automatically restart system calls interrupted signal sa.sa_handler = sigint_handler;  if (sigaction(sigint, &sa, null) == -1) {   printf("could not establish handler\n");   exit(-1); // or } 

and perhaps in getline(), in infinite loop, establish check see if sigint has been caught:

for (;;) {   if (sigint_caught) {     return null;   }    // ... 

and in run_interactive() call can check return value with check see if sigint caught:

// ...  get_line(&line);  if (line == null && sigint_caught) {   sigint_caught = 0; // allow caught again   free(line);   continue; // or something; go next iteration of loop } else if (line[0] == '\0') {   free(line);   break; } else {   // rest of code 

didn't test can't guarantee it'll work, since question pretty broad (having through more of code etc.), gives enough of idea can in situation. perhaps pretty naive solution might meet needs. more robust perhaps source code popular shells bash or zsh.

for example, 1 thing can happen fgetc() might block since there no new data in stdin, , might when signal sent. fgetc() interrupted , errno eintr, add check in getline():

c = fgetc(stdin);  // make sure #include <errno.h> if (errno == eintr && sigint_caught)   return null; 

this happen if don't set sa_flags sa_restart. if do, fgetc should automatically restart , continue blocking until new input received, may or may not want.


Comments

Popular posts from this blog

android - getbluetoothservice() called with no bluetoothmanagercallback -

sql - ASP.NET SqlDataSource, like on SelectCommand -

ios - Undefined symbols for architecture armv7: "_OBJC_CLASS_$_SSZipArchive" -