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

  int retcode;

  use_timeout = (argc >= 2 && strcmp(argv[1], "-t") == 0);
  if (is_help_requested(argc, argv)) {
    usage(argv[0], "");
  }

  timeout.tv_sec  = (time_t) 200;
  timeout.tv_nsec = (long) 0;
  if (use_timeout && argc >= 3) {
    int t;
    sscanf(argv[2], "%d", &t);
    timeout.tv_sec = (time_t) t;
  }
  printf("timout(%ld sec %ld msec)\n", (long) timeout.tv_sec, (long) timeout.tv_nsec);
  
  pthread_t thread[N_THREADS];
  int tidx[N_THREADS];
  for (size_t i = 0; i < N_THREADS; i++) {
    tidx[i] = (int) i;
    retcode = pthread_create(thread + i, NULL, run, (void *) (tidx + i)); // equivalent: ((void *) tidx) + sizeof(int)*i
    handle_thread_error(retcode, "creating thread failed", PROCESS_EXIT);
  }
  printf("in parent: setting up\n");

  pthread_mutex_init(&mutex, NULL);
  sleep(2);
  printf("in parent: getting mutex\n");
  if (use_timeout) {
    while (TRUE) {
      retcode = pthread_mutex_timedlock(&mutex, &timeout);
      if (retcode == 0) {
        break;
      } else if (retcode == ETIMEDOUT) {
        printf("timed out in parent\n");
      } else {
        handle_thread_error(retcode, "parent failed (timed)lock", PROCESS_EXIT);
      }
    }
  } else {
    retcode = pthread_mutex_lock(&mutex);
    handle_thread_error(retcode, "parent failed lock", PROCESS_EXIT);
  }
  printf("parent got mutex\n");
  sleep(5);
  printf("parent releases mutex\n");
  pthread_mutex_unlock(&mutex);
  printf("parent released mutex\n");
  printf("parent waiting for child to terminate\n");
  for (size_t i = 0; i < N_THREADS; i++) {
    retcode = pthread_join(thread[i], NULL);
    handle_thread_error(retcode, "join failed", PROCESS_EXIT);
    printf("joined thread %d\n", (int) i);
  }
  pthread_mutex_destroy(&mutex);
  printf("done\n");
  exit(0);
}
int main(int argc, char *argv[]) {

    int retcode;

    if (is_help_requested(argc, argv)) {
        usage(argv[0], "");
    }

    int sock;                        /* Socket descriptor */
    struct sockaddr_in server_address; /* Square server address */
    unsigned short server_port;     /* Square server port */
    char *server_ip;                    /* Server IP address (dotted quad) */
    char receive_buffer[RCVBUFSIZE];     /* Buffer for received string */

    if (argc < 2 || argc > 3) {    /* Test for correct number of arguments */
        usage(argv[0], "wrong number of arguments");
    }

    server_ip = argv[1];             /* First arg: server IP address (dotted quad) */

    if (argc == 3) {
        server_port = atoi(argv[2]); /* Use given port, if any */
    } else {
        server_port = 7000;  /* 7000 is a free port */
    }

    /* Create a reliable, stream socket using TCP */
    sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    handle_error(sock, "socket() failed", PROCESS_EXIT);

    /* Construct the server address structure */
    memset(&server_address, 0, sizeof(server_address));     /* Zero out structure */
    server_address.sin_family      = AF_INET;             /* Internet address family */
    server_address.sin_addr.s_addr = inet_addr(server_ip);   /* Server IP address */
    server_address.sin_port        = htons(server_port); /* Server port: htons host to network byte order */

    /* Establish the connection to the square server */
    retcode = connect(sock, (struct sockaddr *) &server_address, sizeof(server_address));
    handle_error(retcode, "connect() failed", PROCESS_EXIT);

    const char *cmd_string = "GETX";
    /* Send the string to the server */
    write_4byte_string(sock, cmd_string);

    /* Receive the same string containing the square back from the server */
    printf("Received: \n");                /* Setup to print the squared string */
    while (TRUE) {

        size_t ulen = read_string_fragmentable(sock, receive_buffer, RCVBUFSIZE, consume);
        if (ulen == 0) {
            break;
        }
    }

    const char *done_string = "DONE";
    write_4byte_string(sock, done_string);
    close(sock);
    printf("\nDONE\n");
    exit(0);
}
int main(int argc, char *argv[]) {

  int retcode;

  if (is_help_requested(argc, argv)) {
    usage(argv[0], "");
  }

  int server_socket;                    /* Socket descriptor for server */
  int client_socket;                    /* Socket descriptor for client */
  struct sockaddr_in server_address; /* Local address */
  struct sockaddr_in client_address; /* Client address */
  unsigned short server_port;     /* Server port */
  unsigned int client_address_len;            /* Length of client address data structure */

  if (argc != 2) {
    /* Test for correct number of arguments */
    usage(argv[0], "wrong number of arguments");
  }
  server_port = atoi(argv[1]);  /* First arg:  local port */

  /* Create socket for incoming connections */
  server_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
  handle_error(server_socket, "socket() failed", PROCESS_EXIT);

  /* Construct local address structure */
  memset(&server_address, 0, sizeof(server_address));   /* Zero out structure */
  server_address.sin_family = AF_INET;                /* Internet address family */
  server_address.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */
  server_address.sin_port = htons(server_port);      /* Local port */

  /* Bind to the local address */
  retcode = bind(server_socket, (struct sockaddr *) &server_address, sizeof(server_address));
  handle_error(retcode, "bind() failed", PROCESS_EXIT);

  /* Mark the socket so it will listen for incoming connections */
  retcode = listen(server_socket, MAXPENDING);
  handle_error(retcode, "listen() failed", PROCESS_EXIT);

  while (TRUE) { /* Run forever */
    /* Set the size of the in-out parameter */
    client_address_len = sizeof(client_address);

    /* Wait for a client to connect */
    client_socket = accept(server_socket, (struct sockaddr *) &client_address, &client_address_len);
    handle_error(client_socket, "accept() failed", PROCESS_EXIT);

    printf("Handling client %s\n", inet_ntoa(client_address.sin_addr));
    /* client_socket is connected to a client! */
    
    pthread_t thread;
    int *client_socket_ptr = (int *) malloc(sizeof(int));
    *client_socket_ptr = client_socket;
    pthread_create(&thread, NULL, handle_tcp_client, client_socket_ptr);
    pthread_detach(thread);
  }
  /* NOT REACHED: */
  exit(0);
}
int main(int argc, char *argv[]) {

  if (is_help_requested(argc, argv)) {
    usage(argv[0], "");
  }

  atexit(exit_handler);
  printf("main stared\n");
  pthread_exit(NULL);
  printf("after exit");
  return 0;
}
int main(int argc, char *argv[]) {

  if (is_help_requested(argc, argv)) {
    usage(argv[0], "");
  }

  out = fopen("myfile", "w");
  fwrite(TEXT, 1, strlen(TEXT), out);
  fwrite(TEXTO, 1, strlen(TEXTO), stdout);
  fwrite(TEXTE, 1, strlen(TEXTE), stderr);
  pthread_t thread1, thread2;
  pthread_create(&thread1, NULL, run, "thread1\n");
  pthread_create(&thread2, NULL, run, "thread2\n");
  run("main\n");
  pthread_join(thread1, NULL);
  pthread_join(thread2, NULL);
  printf("joined all\n");
  exit(0);
}
int main(int argc, char *argv[]) {

  time_t t_start = time(NULL);

  if (is_help_requested(argc, argv)) {
    usage(argv[0], "");
  }

  if (argc > 2) {
    usage(argv[0], "too many arguments");
  }

  int retcode = 0;

  create_if_missing(REF_FILE, S_IRUSR | S_IWUSR);

  key_t shm_key = ftok(REF_FILE, 1);
  if (shm_key < 0) {
    handle_error(-1, "ftok failed", PROCESS_EXIT);
  }

  key_t sem_key = ftok(REF_FILE, 2);
  if (sem_key < 0) {
    handle_error(-1, "ftok failed", PROCESS_EXIT);
  }

  if (argc == 2 && strcmp(argv[1], "-s") == 0) {
    printf("setting up IPC\n");
    int shm_id = create_shm(shm_key, "create", "shmget failed", IPC_CREAT);
    shmid_for_cleanup = shm_id;
    int semaphore_id = create_sem(sem_key, SEM_SIZE, "create", "semget (data) failed", IPC_CREAT);
    semid_for_cleanup = semaphore_id;

    show_sem_ctl(semaphore_id, 0, "semaphore before setup");
    setup_sem(semaphore_id, "semaphore setup failed");
    show_sem_ctl(semaphore_id, 0, "semaphore after setup");
    printf("done\n");
    exit(0);
  }

  int shm_id = create_shm(shm_key, "create", "shmget failed", 0);
  shmid_for_cleanup = shm_id;
  int semaphore_id = create_sem(sem_key, SEM_SIZE, "create", "semget failed", 0);
  semid_for_cleanup = semaphore_id;

  if (argc == 2 && strcmp(argv[1], "-c") == 0) {
    printf("cleaning up IPC\n");
    cleanup();
    exit(0);
  }

  char *name = "";
  if (argc == 2) {
    name = argv[1];
  }

  struct data *shm_data = (struct data *) shmat(shm_id, NULL, 0);

  time_t total_data_semops_wait = 0;
  char buffer[BUF_SIZE];
  long *counter = shm_data->counter;

  while (TRUE) {
    ssize_t size_read = read(STDIN_FILENO, buffer, BUF_SIZE);
    if (size_read == 0) {
      /* end of file */
      break;
    }
    handle_error(size_read, "error while reading stdin", PROCESS_EXIT);
    int i;
    for (i = 0; i < size_read; i++) {
      unsigned char c = buffer[i];
      unsigned int  ck = c % SEM_SIZE;
      struct sembuf semops_write;
      semops_write.sem_num = ck;
      semops_write.sem_op  = -SEM_LIMIT;
      semops_write.sem_flg = SEM_UNDO;
      time_t t0 = time(NULL);
      // show_sem_ctl(semaphore_id, ck, "reserving write semaphore");
      retcode = semop(semaphore_id, &semops_write, 1);
      handle_error(retcode, "error while getting write-semaphore", PROCESS_EXIT);
      // show_sem_ctl(semaphore_id, ck, "write semaphore reserved");
      time_t dt = time(NULL) - t0;
      total_data_semops_wait += dt;
      counter[c]++;
      semops_write.sem_num = ck;
      semops_write.sem_op  = SEM_LIMIT;
      semops_write.sem_flg = SEM_UNDO;
      // show_sem_ctl(semaphore_id, ck, "freeing write semaphore");
      retcode = semop(semaphore_id, &semops_write, 1);
      handle_error(retcode, "error while releasing write-semaphore", PROCESS_EXIT);
      // show_sem_ctl(semaphore_id, ck, "write semaphore freed");
    }
  }

  time_t total_duration = time(NULL) - t_start;

  unsigned int i;

  char output_buffer[16384];
  char *output_ptr = output_buffer;
  int n;
  int m = 0;
  n = sprintf(output_ptr, "------------------------------------------------------------\n");
  output_ptr += n; m += n;
  n = sprintf(output_ptr, "%s: pid=%ld\n", name, (long) getpid());
  output_ptr += n; m += n;
  n = sprintf(output_ptr, "total wait for data: ~ %ld sec; total duration: ~ %ld\n", (long) total_data_semops_wait, (long) total_duration);
  output_ptr += n; m += n;
  n = sprintf(output_ptr, "------------------------------------------------------------\n");
  output_ptr += n; m += n;
  for (i = 0; i < ALPHA_SIZE; i++) {
    struct sembuf semops_read;
    unsigned int  ck = (i % SEM_SIZE);
    semops_read.sem_num = ck;
    semops_read.sem_op  = -1;
    semops_read.sem_flg = SEM_UNDO;
    retcode = semop(semaphore_id, &semops_read, 1);
    handle_error(retcode, "error while getting read-semaphore", PROCESS_EXIT);
    long *counter = shm_data->counter;
    long val = counter[i];
    semops_read.sem_op  = 1;
    retcode = semop(semaphore_id, &semops_read, 1);
    handle_error(retcode, "error while releasing read-semaphore", PROCESS_EXIT);
    if (! (i & 007)) {
      n = sprintf(output_ptr, "\n");
      output_ptr += n; m += n;
    }
    if ((i & 0177) < 32 || i == 127) {
      n = sprintf(output_ptr, "\\%03o: %10ld    ", i, val);
      output_ptr += n; m += n;
    } else {
      n = sprintf(output_ptr, "%4c: %10ld    ", (char) i, val);
      output_ptr += n; m += n;
    }
  }
  n = sprintf(output_ptr, "\n\n");
  output_ptr += n; m += n;
  n = sprintf(output_ptr, "------------------------------------------------------------\n\n");
  output_ptr += n; m += n;
  write(STDOUT_FILENO, output_buffer, (size_t) m);

  exit(0);
}
int main(int argc, char *argv[]) {

  int retcode;
  if (is_help_requested(argc, argv)) {
    usage(argv[0], "Help:");
  }

  char *server_ip = get_ip_with_default(argc, argv);
  unsigned short server_port = get_port_with_default(argc, argv);
  get_logging_properties(argc, argv);

  int sock;                        
  int interactive = TRUE; 
  int firstRun = TRUE;
  char *cmd;
  int cmdLen;

  int i;
  for (i = 2; i < argc; i++)  {
    if (strcmp(argv[i], "-c") == 0)  {
      if (i + 2 <= argc )  {

        interactive = FALSE;
        i++;
        cmd = argv[i];
        cmdLen = strlen(cmd);
      } else {
        usage(argv[0], "please provide a CMD if you're using -c");
      }
    } else if (strcmp(argv[i], "-C") == 0)  {
      if (i + 2 <= argc )  {

        if(interactive == TRUE) {
          usage(argv[0], "please provide a first line withe -c if you're using -C");
        }

        i++;
        cmd = join_with_seperator(cmd, argv[i], "\n");
        cmdLen = strlen(cmd);
      } else {
        usage(argv[0], "please provide a CMD if you're using -c");
      }
    }
  }

  while (firstRun || interactive ) {

    char *send;
    if (interactive  != TRUE){
      send = join_with_seperator(cmd, "", "\n");
    } else {
      char line[2048];
      char c;
      char lastc = '\n';
      int i = 0;

      printf("CMD=");
      fflush(stdout);

      while (EOF!= ( c = getc(stdin))) {

        if (lastc == '\n' && c == '\n') {
          break;
        }
        lastc = c;
        line[i]  = c;
        i++;
      }
      line[i]  = '\000';

      int n = strlen(line);

      if (n == 0) {
        continue;
      }
      if (strcmp(line, "QUIT\n") == 0) {
        printf("terminated by user\n");
        break;
      }
      
      send = line;
    }

    sock = create_client_socket(server_port, server_ip);

    log_debug("Sendling: '%s'\n", send);
    write_to_socket(sock, send);

    char *buffer_ptr[0];

    // Receive command from server 
    size_t received_msg_size = read_from_socket(sock, buffer_ptr);
    handle_error(received_msg_size, "recive failed", THREAD_EXIT);

    printf("Response=%s \n", *buffer_ptr);
    free(*buffer_ptr);

    close(sock);
    firstRun = FALSE;
  }
  exit(0);
}
int main(int argc, char *argv[]) {
  int retcode;
  pthread_t *thread;

  char *argv0 = argv[0];
  if (is_help_requested(argc, argv)) {
    usage(argv0, "");
  }

  if (argc != 3 && argc != 5) {
    printf("found %d arguments\n", argc - 1);
    usage(argv0, "wrong number of arguments");
  }

  int opt_idx = 1;
  int n_idx = 2;
  use_psort = FALSE;

  if (argc == 5) {
    if (strcmp(argv[1], "-p") != 0) {
      usage(argv0, "wrong first option");
    }
    psort_internal_threads = atoi(argv[2]);
    opt_idx+=2;
    n_idx+=2;
    use_psort = TRUE;
  }

  /* TODO consider using getopt instead!! */
  char *argv_opt = argv[opt_idx];
  if (strlen(argv_opt) != 2 || argv_opt[0] != '-') {
    usage(argv0, "wrong option");
  }
  char opt_char = argv_opt[1];
  switch (opt_char) {
  case 'h' :
    selected_mt_sort_type = MT_HEAP_SORT;
    break;
  case 't' :
    selected_mt_sort_type = MT_TERNARY_HEAP_SORT;
    break;
  case 'f' :
    selected_mt_sort_type = MT_FLASH_SORT;
    break;
  case 'b' :
    selected_mt_sort_type = MT_FLASH_SORT_BIN;
    break;
  case 'q' :
    selected_mt_sort_type = MT_QUICK_SORT;
    break;
  case 'i' :
    selected_mt_sort_type = MT_INSERTION_SORT;
    break;
  case 'm' :
    selected_mt_sort_type = MT_MERGE_SORT;
    break;
  default:
    usage(argv0, "wrong option: only -q and -h supported");
    break;
  }

  thread_count = atoi(argv[n_idx]);
  if (thread_count < 1 || thread_count > 1024) {
    printf("running with %d threads\n", thread_count);
    usage(argv[0], "wrong number of threads");
  }

  struct string_array content = read_to_array(STDIN_FILENO);
  int len_per_thread = content.len / thread_count;

  thread = (pthread_t *) malloc(thread_count * sizeof(pthread_t));
  segments = (struct thread_arg *) malloc(thread_count * sizeof(struct thread_arg));

  retcode = pthread_barrier_init(&barrier, NULL, thread_count);
  handle_thread_error(retcode, "pthread_barrier_init", PROCESS_EXIT);
  retcode = pthread_mutex_init(&output_mutex, NULL);
  handle_thread_error(retcode, "pthread_mutex_init", PROCESS_EXIT);

  int rest_len = content.len;
  char **ptr = content.strings;

  for (int i = 0; i < thread_count; i++) {
    segments[i].thread_idx = i;
    segments[i].arr.strings = ptr;
    if (i == thread_count-1) {
      segments[i].arr.len     = rest_len;
    } else {
      segments[i].arr.len = len_per_thread;
    }
    ptr += len_per_thread;
    rest_len -= len_per_thread;

    /* pthread_mutex_lock(&output_mutex); */
    /* printf("main: starting thread %d\n", i); */
    /* pthread_mutex_unlock(&output_mutex); */

    retcode = pthread_create(&(thread[i]), NULL, thread_run, &(segments[i]));
    handle_thread_error(retcode, "pthread_create", PROCESS_EXIT);

    /* pthread_mutex_lock(&output_mutex); */
    /* printf("main: started %d\n", i); */
    /* pthread_mutex_unlock(&output_mutex); */
  }

  for (int i = 0; i < thread_count; i++) {
    /* printf("main: joining thread %d\n", i); */
    retcode = pthread_join(thread[i], NULL);
    handle_thread_error(retcode, "pthread_join", PROCESS_EXIT);
    /* printf("main: joined thread %d\n", i); */
  }
  char_ptr *strings = segments[0].arr.strings;
  int len = segments[0].arr.len;
  for (int i = 0; i < len; i++) {
    /* printf("%5d \"%s\"\n", i, strings[i]); */
    printf("%s\n", strings[i]);
  }
  pthread_barrier_destroy(&barrier);
  // printf("DONE\n");
  exit(0);
}