/*
 * tsh - 一个支持作业控制的微型shell程序
 *
 * 功能特性：
 * - 支持内置命令：jobs, fg, bg
 * - 支持前台/后台作业控制
 * - 支持作业状态管理(运行中/停止)
 * - 处理Ctrl-C(中断)、Ctrl-Z(停止)等信号
 * - 支持最多16个并发作业
 *
 * 主要数据结构：
 * - job_t: 表示一个作业，包含PID、作业ID、状态和命令行
 * - jobs数组: 保存所有作业的列表
 *
 * 作业状态说明：
 * - UNDEF: 未定义
 * - FG: 前台运行
 * - BG: 后台运行
 * - ST: 已停止
 *
 * ChengJingyu 202302723005
 */

/* 最大命令行长度 */
#define MAXLINE 1024 /* max line size */
/* 每条命令最大参数数量 */
#define MAXARGS 128 /* max args on a command line */
/* 最大并发作业数 */
#define MAXJOBS 16 /* max jobs at any point in time */
/* 最大作业ID值 */
#define MAXJID 1 << 16 /* max job ID */

/* 作业状态常量定义 */
#define UNDEF 0 /* 未定义状态 */
#define FG 1    /* 前台运行状态 */
#define BG 2    /* 后台运行状态 */
#define ST 3    /* 停止状态 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
// #include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>

/* Misc manifest constants */
#define MAXLINE 1024   /* max line size */
#define MAXARGS 128    /* max args on a command line */
#define MAXJOBS 16     /* max jobs at any point in time */
#define MAXJID 1 << 16 /* max job ID */

/* Job states */
#define UNDEF 0 /* undefined */
#define FG 1    /* running in foreground */
#define BG 2    /* running in background */
#define ST 3    /* stopped */

/*
 * Jobs states: FG (foreground), BG (background), ST (stopped)
 * Job state transitions and enabling actions:
 *     FG -> ST  : ctrl-z
 *     ST -> FG  : fg command
 *     ST -> BG  : bg command
 *     BG -> FG  : fg command
 * At most 1 job can be in the FG state.
 */

/* 全局变量定义 */
extern char **environ;   /* 环境变量数组，定义在libc中 */
char prompt[] = "tsh> "; /* 命令行提示符(请勿修改) */
int verbose = 0;         /* 调试模式标志，为真时打印额外信息 */
int nextjid = 1;         /* 下一个要分配的作业ID */
char sbuf[MAXLINE];      /* 用于构造sprintf消息的缓冲区 */

/* 作业结构体定义 */
struct job_t {
  pid_t pid;             /* 进程ID */
  int jid;               /* 作业ID [1, 2, ...] */
  int state;             /* 状态: UNDEF(未定义), BG(后台), FG(前台), ST(停止) */
  char cmdline[MAXLINE]; /* 命令行字符串 */
};
struct job_t jobs[MAXJOBS]; /* 作业列表数组 */
/* 全局变量定义结束 */

/* 函数原型声明 */

/* 需要实现的核心功能函数 */
void eval(char *cmdline);     /* 评估并执行命令行 */
int builtin_cmd(char **argv); /* 执行内置命令 */
void do_bgfg(char **argv);    /* 处理fg/bg命令 */
void waitfg(pid_t pid);       /* 等待前台作业完成 */

/* 信号处理函数 */
void sigchld_handler(int sig); /* 子进程状态改变处理 */
void sigtstp_handler(int sig); /* Ctrl-Z处理 */
void sigint_handler(int sig);  /* Ctrl-C处理 */

/* 提供的辅助函数 */
int parseline(const char *cmdline, char **argv); /* 解析命令行 */
void sigquit_handler(int sig);                   /* 退出信号处理 */

/* 作业管理函数 */
void clearjob(struct job_t *job);  /* 清除作业条目 */
void initjobs(struct job_t *jobs); /* 初始化作业列表 */
int maxjid(struct job_t *jobs);    /* 获取最大作业ID */
int addjob(struct job_t *jobs, pid_t pid, int state,
           char *cmdline);                              /* 添加作业 */
int deletejob(struct job_t *jobs, pid_t pid);           /* 删除作业 */
pid_t fgpid(struct job_t *jobs);                        /* 获取前台作业PID */
struct job_t *getjobpid(struct job_t *jobs, pid_t pid); /* 通过PID获取作业 */
struct job_t *getjobjid(struct job_t *jobs, int jid);   /* 通过JID获取作业 */
int pid2jid(pid_t pid);                                 /* PID转JID */
void listjobs(struct job_t *jobs);                      /* 列出所有作业 */
void listjob(struct job_t *job);                        /* 列出单个作业 */

/* 工具函数 */
void usage(void);           /* 打印用法信息 */
void unix_error(char *msg); /* Unix风格错误处理 */
void app_error(char *msg);  /* 应用风格错误处理 */
typedef void handler_t(int);
handler_t *Signal(int signum, handler_t *handler); /* 信号处理包装函数 */

/*
 * main - shell的主循环函数
 *
 * 功能：
 * 1. 解析命令行参数
 * 2. 安装信号处理程序
 * 3. 初始化作业列表
 * 4. 进入读取-评估循环
 *
 * 参数：
 * argc - 参数个数
 * argv - 参数数组
 *
 * 返回值：
 * 正常情况不会返回，若返回则退出码为0
 */
int main(int argc, char **argv) {
  char c;
  char cmdline[MAXLINE];
  int emit_prompt = 1; /* 是否显示提示符，默认为1(显示) */

  /* 将标准错误重定向到标准输出，确保所有输出都通过管道传递 */
  dup2(1, 2);

  /* 解析命令行参数 */
  while ((c = getopt(argc, argv, "hvp")) != EOF) {
    switch (c) {
    case 'h': /* 打印帮助信息 */
      usage();
      break;
    case 'v': /* 启用详细输出模式 */
      verbose = 1;
      break;
    case 'p': /* 不显示提示符(用于自动化测试) */
      emit_prompt = 0;
      break;
    default:
      usage();
    }
  }

  /* 安装信号处理程序 */

  /* 需要实现的信号处理函数 */
  Signal(SIGINT, sigint_handler);   /* 处理Ctrl-C中断信号 */
  Signal(SIGTSTP, sigtstp_handler); /* 处理Ctrl-Z停止信号 */
  Signal(SIGCHLD, sigchld_handler); /* 处理子进程状态变化 */

  /* 提供一个干净终止shell的方式 */
  Signal(SIGQUIT, sigquit_handler);

  /* 初始化作业列表 */
  initjobs(jobs);

  /* shell主循环：读取-评估-执行 */
  while (1) {
    /* 读取命令行输入 */
    if (emit_prompt) {
      printf("%s", prompt); /* 显示提示符 */
      fflush(stdout);
    }
    if ((fgets(cmdline, MAXLINE, stdin) == NULL) && ferror(stdin))
      app_error("fgets error"); /* 读取错误处理 */
    if (feof(stdin))            /* 处理文件结束(Ctrl-D) */
    {
      fflush(stdout);
      exit(0);
    }

    /* 评估并执行命令行 */
    eval(cmdline);
    fflush(stdout);
    fflush(stdout);
  }

  exit(0); /* 控制流不会到达这里 */
}

/*
 * eval - 评估并执行用户输入的命令行
 *
 * 功能：
 * 1. 如果是内置命令(quit/jobs/bg/fg)，直接执行
 * 2. 否则fork子进程执行命令
 * 3. 如果是前台作业，等待其完成
 * 4. 设置独立的进程组，防止后台作业接收终端信号
 *
 * 参数：
 * cmdline - 用户输入的命令行字符串
 */
void eval(char *cmdline) {
  char *argv[MAXARGS]; /* execve()的参数列表 */
  char buf[MAXLINE];   /* 存储修改后的命令行 */
  int bg;              /* 作业是否应该在后台运行 */
  pid_t pid;           /* 子进程ID */
  sigset_t mask_all, mask_one, pre;

  /* 初始化信号屏蔽集合 */
  sigfillset(&mask_all);         /* 屏蔽所有信号 */
  sigemptyset(&mask_one);        /* 初始化空信号集 */
  sigaddset(&mask_one, SIGCHLD); /* 添加SIGCHLD信号 */

  /* 解析命令行 */
  strcpy(buf, cmdline);
  bg = parseline(buf, argv); /* 解析命令行并判断是否后台运行 */

  if (argv[0] == NULL) /* 忽略空行 */
    return;

  /* 处理非内置命令 */
  if (!builtin_cmd(argv)) {
    /* 阻塞SIGCHLD信号，防止addjob前收到SIGCHLD */
    sigprocmask(SIG_BLOCK, &mask_one, &pre);

    /* 创建子进程执行命令 */
    if ((pid = fork()) == 0) { /* 子进程执行用户作业 */
      setpgid(0, 0);           /* 设置新的进程组，防止后台作业接收终端信号 */
      sigprocmask(SIG_SETMASK, &pre, NULL); /* 恢复信号屏蔽 */

      /* 执行命令 */
      if (execve(argv[0], argv, environ) < 0) {
        printf("%s: Command not found.\n", argv[0]);
        exit(0);
      }
    }

    /* 父进程添加作业到作业列表 */
    addjob(jobs, pid, bg ? BG : FG, cmdline); /* 添加作业到作业列表 */
    sigprocmask(SIG_SETMASK, &pre, NULL);     /* 恢复信号屏蔽 */

    /* 处理前台/后台作业 */
    if (!bg) {
      waitfg(pid); /* 等待前台作业完成 */
    } else {
      listjob(getjobpid(jobs, pid)); /* 列出后台作业信息 */
    }
  }
  return;
}

/*
 * parseline - 解析命令行并构建argv数组
 *
 * 功能：
 * 1. 解析命令行参数，处理单引号内的内容作为一个参数
 * 2. 判断是否为后台作业(&结尾)
 *
 * 参数：
 * cmdline - 输入的命令行字符串
 * argv - 输出的参数数组
 *
 * 返回值：
 * 如果是后台作业返回1，否则返回0
 */
int parseline(const char *cmdline, char **argv) {
  static char array[MAXLINE]; /* 存储命令行副本 */
  char *buf = array;          /* 遍历命令行的指针 */
  char *delim;                /* 参数分隔符位置 */
  int argc;                   /* 参数个数 */
  int bg;                     /* 是否为后台作业 */

  /* 初始化处理 */
  strcpy(buf, cmdline);
  buf[strlen(buf) - 1] = ' ';   /* 替换末尾的换行符为空格 */
  while (*buf && (*buf == ' ')) /* 跳过前导空格 */
    buf++;

  /* 构建argv参数列表 */
  argc = 0;
  if (*buf == '\'') { /* 处理单引号参数 */
    buf++;
    delim = strchr(buf, '\'');
  } else {
    delim = strchr(buf, ' ');
  }

  /* 循环解析所有参数 */
  while (delim) {
    argv[argc++] = buf; /* 添加参数到数组 */
    *delim = '\0';      /* 终止当前参数 */
    buf = delim + 1;
    while (*buf && (*buf == ' ')) /* 跳过空格 */
      buf++;

    /* 查找下一个参数分隔符 */
    if (*buf == '\'') {
      buf++;
      delim = strchr(buf, '\'');
    } else {
      delim = strchr(buf, ' ');
    }
  }
  argv[argc] = NULL; /* 参数列表以NULL结尾 */

  if (argc == 0) /* 忽略空行 */
    return 1;

  /* 检查是否为后台作业 */
  if ((bg = (*argv[argc - 1] == '&')) != 0) {
    argv[--argc] = NULL; /* 移除&符号 */
  }
  return bg;
}

/*
 * builtin_cmd - 执行内置命令
 *
 * 功能：
 * 1. 处理quit命令: 退出shell
 * 2. 处理jobs命令: 列出所有作业
 * 3. 处理fg/bg命令: 转发给do_bgfg处理
 * 4. 忽略单独的&符号
 *
 * 参数：
 * argv - 命令参数数组
 *
 * 返回值：
 * 如果是内置命令返回1，否则返回0
 */
int builtin_cmd(char **argv) {
  if (!strcmp(argv[0], "quit")) /* 退出命令 */
    exit(0);
  if (!strcmp(argv[0], "&")) /* 忽略单独的& */
    return 1;
  if (!strcmp(argv[0], "jobs")) { /* 列出作业 */
    listjobs(jobs);
    return 1;
  }
  if (!strcmp(argv[0], "fg") || !strcmp(argv[0], "bg")) { /* 前台/后台命令 */
    do_bgfg(argv);
    return 1;
  }
  return 0; /* 不是内置命令 */
}

/*
 * do_bgfg - 执行bg和fg内置命令
 *
 * 功能：
 * 1. 解析PID或%JID参数
 * 2. 将作业转为前台或后台运行
 * 3. 发送CONT信号继续作业
 *
 * 参数：
 * argv - 命令参数数组
 */
void do_bgfg(char **argv) {
  struct job_t *job;
  char *name = argv[0]; /* 命令名(fg/bg) */
  char *num = argv[1];  /* PID或%JID参数 */

  /* 参数验证 */
  if (num == NULL) {
    printf("%s command requires PID or %%jobid argument\n", name);
    return;
  }

  /* 处理%JID格式 */
  if (*num == '%') {
    num = num + 1;                                  /* 跳过% */
    if (strspn(num, "0123456789") != strlen(num)) { /* 检查数字 */
      printf("%s: argument must be a PID or %%jobid\n", name);
      return;
    }
    job = getjobjid(jobs, atoi(num)); /* 通过JID获取作业 */
    if (job == NULL) {
      printf("%%%s: No such job\n", num);
      return;
    }
  }
  /* 处理PID格式 */
  else {
    if (strspn(num, "0123456789") != strlen(num)) { /* 检查数字 */
      printf("%s: argument must be a PID or %%jobid\n", name);
      return;
    }
    job = getjobpid(jobs, (pid_t)atoi(num)); /* 通过PID获取作业 */
    if (job == NULL) {
      printf("(%s): No such process\n", num);
      return;
    }
  }

  /* 执行fg/bg命令 */
  if (!strcmp(argv[0], "fg")) {        /* 前台命令 */
    kill(job->pid, SIGCONT);           /* 发送继续信号 */
    job->state = FG;                   /* 设为前台作业 */
    waitfg(job->pid);                  /* 等待前台作业完成 */
  } else if (!strcmp(argv[0], "bg")) { /* 后台命令 */
    kill(job->pid, SIGCONT);           /* 发送继续信号 */
    job->state = BG;                   /* 设为后台作业 */
    listjob(job);                      /* 列出作业信息 */
  }
}

/*
 * waitfg - 等待前台作业完成
 *
 * 功能：
 * 1. 阻塞当前进程，直到指定的前台作业完成
 * 2. 使用sigsuspend安全地等待信号，避免竞争条件
 * 3. 处理SIGCHLD信号，确保前台作业结束时能及时检测到
 *
 * 参数：
 * pid - 要等待的前台作业的进程ID
 *
 * 实现说明：
 * 1. 使用信号屏蔽确保原子性操作
 * 2. 通过检查作业列表中的前台作业状态来判断是否继续等待
 * 3. 使用sigsuspend替代pause，避免竞争条件
 */
void waitfg(pid_t pid) {
  /*
   * 示例说明(trace06.txt):
   *  ./myspin 4  # 子进程前台执行，主进程进入pause
   *  INT #
   *  (1)
   * CTRL_C发送INT信号给主进程，中断pause状态,进入INT处理函数,
   * 该函数把INT信号转发给前台子进程，处理完成后返回上次中断的位置(2)。
   *  (2)
   * INT中断处理函数返回后，继续执行下一条指令,
   * 发现while循环条件依然成立又进入pause.
   *  (3)
   * 前台子进程被INT信号终止结束生命发送CHLD信号给主进程，
   * 中断pause状态，进入CHLD处理函数，
   * 该函数回收僵尸进程，删除对应的job,处理完成后返回上次中断的位置。
   *  (4)
   * CHLD中断返回后，继续执行下一条指令，
   * 发现while循环条件不再成立，跳出while循环。
   *
   * 注意：如果(3)的中断位置在while条件执行后，pause执行前，
   * 那么使用pause会导致中断处理函数返回后一直pause，
   * 而sigsuspend可以避免这种情况。
   */

  sigset_t mask, prev;
  sigemptyset(&mask);
  sigaddset(&mask, SIGCHLD);            /* 屏蔽SIGCHLD信号 */
  sigprocmask(SIG_BLOCK, &mask, &prev); /* 设置信号屏蔽 */

  /* 循环等待前台作业完成 */
  while (fgpid(jobs)) {
    if (verbose)
      printf("%ld pausing...\n", time(NULL));
    sigsuspend(&prev); /* 原子地解除屏蔽并等待信号 */
    if (verbose)
      printf("%ld interrupt return\n", time(NULL));
  }

  sigprocmask(SIG_SETMASK, &prev, NULL); /* 恢复原始信号屏蔽 */
}

/*****************
 * Signal handlers
 *****************/

/*
 * sigchld_handler - 处理子进程状态变化的信号处理函数
 *
 * 功能：
 * 1. 当子进程终止(成为僵尸进程)或停止时，内核会发送SIGCHLD信号
 * 2. 回收所有可用的僵尸子进程
 * 3. 不等待其他正在运行的子进程终止
 *
 * 参数：
 * sig - 信号编号
 */
void sigchld_handler(int sig) {
  if (verbose)
    printf("%ld interrupt by SIGCHLD(%d)\n", time(NULL), sig);

  int olderrno = errno;
  pid_t pid;
  int status;

  /* 循环处理所有终止或停止的子进程 */
  while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED | WCONTINUED)) > 0) {
    if (WIFEXITED(status)) {          /* 子进程正常退出 */
      deletejob(jobs, pid);           /* 从作业列表中删除作业 */
    } else if (WIFSIGNALED(status)) { /* 子进程被信号终止 */
      printf("Job [%d] (%d) terminated by signal %d\n", pid2jid(pid), pid,
             WTERMSIG(status));
      deletejob(jobs, pid);          /* 从作业列表中删除作业 */
    } else if (WIFSTOPPED(status)) { /* 子进程被信号停止 */
      printf("Job [%d] (%d) stopted by signal %d\n", pid2jid(pid), pid,
             WSTOPSIG(status));
      getjobpid(jobs, pid)->state = ST; /* 更新作业状态为停止 */
    } else if (WIFCONTINUED(status)) {  /* 子进程被继续 */
      /* 目前不做特殊处理 */
    }
  }

  /* 错误处理 */
  if (pid == -1 && errno != ECHILD) {
    unix_error("sigchld_handler error");
  }
  errno = olderrno; /* 恢复errno */
}

/*
 * sigint_handler - 处理Ctrl-C中断信号的信号处理函数
 *
 * 功能：
 * 1. 当用户在键盘输入Ctrl-C时，内核会发送SIGINT信号
 * 2. 将信号转发给前台作业
 *
 * 参数：
 * sig - 信号编号
 */
void sigint_handler(int sig) {
  if (verbose)
    printf("%ld interrupt by SIGINT(%d)\n", time(NULL), sig);

  int olderrno = errno;
  pid_t pid = fgpid(jobs);       /* 获取前台作业的PID */
  if (kill(pid, SIGINT) == -1) { /* 向前台作业发送SIGINT信号 */
    unix_error("sigint_handler");
  }
  errno = olderrno; /* 恢复errno */
}

/*
 * sigtstp_handler - 处理Ctrl-Z停止信号的信号处理函数
 *
 * 功能：
 * 1. 当用户在键盘输入Ctrl-Z时，内核会发送SIGTSTP信号
 * 2. 将信号转发给前台作业使其停止
 *
 * 参数：
 * sig - 信号编号
 */
void sigtstp_handler(int sig) {
  if (verbose) {
    printf("%ld interrupt by SIGSTP(%d)\n", time(NULL), sig);
  }
  int olderrno = errno;
  pid_t pid = fgpid(jobs);      /* 获取前台作业的PID */
  if (kill(pid, SIGTSTP) == -1) /* 向前台作业发送SIGTSTP信号 */
    unix_error("sigtstp_handler");
  errno = olderrno; /* 恢复errno */
}

/*********************
 * End signal handlers
 *********************/

/***********************************************
 * Helper routines that manipulate the job list
 **********************************************/

/* clearjob - Clear the entries in a job struct */
void clearjob(struct job_t *job) {
  job->pid = 0;
  job->jid = 0;
  job->state = UNDEF;
  job->cmdline[0] = '\0';
}

/* initjobs - Initialize the job list */
void initjobs(struct job_t *jobs) {
  int i;

  for (i = 0; i < MAXJOBS; i++)
    clearjob(&jobs[i]);
}

/* maxjid - Returns largest allocated job ID */
int maxjid(struct job_t *jobs) {
  int i, max = 0;

  for (i = 0; i < MAXJOBS; i++)
    if (jobs[i].jid > max)
      max = jobs[i].jid;
  return max;
}

/*
 * addjob - 添加作业到作业列表
 *
 * 功能：
 * 1. 将新作业添加到第一个空闲位置
 * 2. 分配作业ID并自动递增
 * 3. 设置作业状态和命令行
 *
 * 参数：
 * jobs - 作业列表数组
 * pid - 进程ID
 * state - 作业状态(UNDEF/FG/BG/ST)
 * cmdline - 命令行字符串
 *
 * 返回值：
 * 成功返回1，失败返回0
 */
int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline) {
  int i;

  if (pid < 1) /* 检查PID有效性 */
    return 0;

  /* 查找空闲位置并添加作业 */
  for (i = 0; i < MAXJOBS; i++) {
    if (jobs[i].pid == 0) { /* 找到空闲位置 */
      jobs[i].pid = pid;
      jobs[i].state = state;
      jobs[i].jid = nextjid++; /* 分配作业ID并递增 */
      if (nextjid > MAXJOBS)   /* 作业ID循环使用 */
        nextjid = 1;
      strcpy(jobs[i].cmdline, cmdline); /* 保存命令行 */
      if (verbose) {                    /* 调试输出 */
        printf("%ld Added job [%d] %d %s\n", time(NULL), jobs[i].jid,
               jobs[i].pid, jobs[i].cmdline);
      }
      return 1; /* 添加成功 */
    }
  }
  printf("Tried to create too many jobs\n"); /* 作业列表已满 */
  return 0;
}

/*
 * deletejob - 从作业列表删除指定PID的作业
 *
 * 功能：
 * 1. 根据PID查找并删除作业
 * 2. 更新下一个可用的作业ID
 *
 * 参数：
 * jobs - 作业列表数组
 * pid - 要删除的进程ID
 *
 * 返回值：
 * 成功返回1，失败返回0
 */
int deletejob(struct job_t *jobs, pid_t pid) {
  int i;

  if (pid < 1) /* 检查PID有效性 */
    return 0;

  /* 查找并删除作业 */
  for (i = 0; i < MAXJOBS; i++) {
    if (jobs[i].pid == pid) { /* 找到匹配的作业 */
      if (verbose) {          /* 调试输出 */
        printf("%ld deleted job [%d] %d %s\n", time(NULL), jobs[i].jid,
               jobs[i].pid, jobs[i].cmdline);
      }
      clearjob(&jobs[i]);         /* 清除作业条目 */
      nextjid = maxjid(jobs) + 1; /* 更新下一个可用的作业ID */
      return 1;                   /* 删除成功 */
    }
  }
  return 0; /* 未找到匹配的作业 */
}

/*
 * fgpid - 获取当前前台作业的PID
 *
 * 功能：
 * 遍历作业列表，查找状态为FG的作业
 *
 * 参数：
 * jobs - 作业列表数组
 *
 * 返回值：
 * 找到返回PID，否则返回0
 */
pid_t fgpid(struct job_t *jobs) {
  int i;

  for (i = 0; i < MAXJOBS; i++)
    if (jobs[i].state == FG)
      return jobs[i].pid;
  return 0;
}

/*
 * getjobpid - 通过PID查找作业
 *
 * 功能：
 * 在作业列表中查找指定PID的作业
 *
 * 参数：
 * jobs - 作业列表数组
 * pid - 要查找的进程ID
 *
 * 返回值：
 * 找到返回作业指针，否则返回NULL
 */
struct job_t *getjobpid(struct job_t *jobs, pid_t pid) {
  int i;

  if (pid < 1)
    return NULL;
  for (i = 0; i < MAXJOBS; i++)
    if (jobs[i].pid == pid)
      return &jobs[i];
  return NULL;
}

/*
 * getjobjid - 通过JID查找作业
 *
 * 功能：
 * 在作业列表中查找指定JID的作业
 *
 * 参数：
 * jobs - 作业列表数组
 * jid - 要查找的作业ID
 *
 * 返回值：
 * 找到返回作业指针，否则返回NULL
 */
struct job_t *getjobjid(struct job_t *jobs, int jid) {
  int i;

  if (jid < 1)
    return NULL;
  for (i = 0; i < MAXJOBS; i++)
    if (jobs[i].jid == jid)
      return &jobs[i];
  return NULL;
}

/*
 * pid2jid - 将PID转换为JID
 *
 * 功能：
 * 查找指定PID对应的作业ID
 *
 * 参数：
 * pid - 进程ID
 *
 * 返回值：
 * 找到返回JID，否则返回0
 */
int pid2jid(pid_t pid) {
  int i;

  if (pid < 1)
    return 0;
  for (i = 0; i < MAXJOBS; i++)
    if (jobs[i].pid == pid) {
      return jobs[i].jid;
    }
  return 0;
}

/*
 * listjobs - 打印所有作业信息
 *
 * 功能：
 * 遍历作业列表，打印所有非空作业的信息
 * 包括JID、PID、状态和命令行
 *
 * 参数：
 * jobs - 作业列表数组
 */
void listjobs(struct job_t *jobs) {
  int i;

  for (i = 0; i < MAXJOBS; i++) {
    if (jobs[i].pid != 0) {
      printf("[%d] (%d) ", jobs[i].jid, jobs[i].pid);
      switch (jobs[i].state) {
      case BG:
        printf("Running ");
        break;
      case FG:
        printf("Foreground ");
        break;
      case ST:
        printf("Stopped ");
        break;
      default:
        printf("listjobs: Internal error: job[%d].state=%d ", i, jobs[i].state);
      }
      printf("%s", jobs[i].cmdline);
    }
  }
}

/*
 * listjob - 打印单个作业信息
 *
 * 功能：
 * 打印指定作业的信息
 * 包括JID、PID和命令行
 *
 * 参数：
 * job - 要打印的作业指针
 */
void listjob(struct job_t *job) {
  if (job->pid != 0) {
    printf("[%d] (%d) %s", job->jid, job->pid, job->cmdline);
  }
}
/******************************
 * end job list helper routines
 ******************************/

/***********************
 * Other helper routines
 ***********************/

/*
 * usage - 打印帮助信息
 *
 * 功能：
 * 打印shell的使用说明和命令行选项
 */
void usage(void) {
  printf("Usage: shell [-hvp]\n");
  printf("   -h   print this message\n");
  printf("   -v   print additional diagnostic information\n");
  printf("   -p   do not emit a command prompt\n");
  exit(1);
}

/*
 * unix_error - Unix风格错误处理
 *
 * 功能：
 * 打印Unix系统调用错误信息并退出程序
 *
 * 参数：
 * msg - 自定义错误消息
 */
void unix_error(char *msg) {
  fprintf(stdout, "%s: %s\n", msg, strerror(errno));
  exit(1);
}

/*
 * app_error - 应用风格错误处理
 *
 * 功能：
 * 打印应用程序错误信息并退出程序
 *
 * 参数：
 * msg - 错误消息
 */
void app_error(char *msg) {
  fprintf(stdout, "%s\n", msg);
  exit(1);
}

/*
 * Signal - 信号处理包装函数
 *
 * 功能：
 * 1. 为指定信号安装信号处理程序
 * 2. 设置SA_RESTART标志，使被信号中断的系统调用自动重启
 * 3. 返回旧的处理程序
 *
 * 参数：
 * signum - 信号编号
 * handler - 信号处理函数
 *
 * 返回值：
 * 旧的信号处理程序
 */
handler_t *Signal(int signum, handler_t *handler) {
  struct sigaction action, old_action;

  action.sa_handler = handler;
  sigemptyset(&action.sa_mask); /* 屏蔽正在处理的信号类型 */
  action.sa_flags = SA_RESTART; /* 如果可能，重启被中断的系统调用 */

  if (sigaction(signum, &action, &old_action) < 0)
    unix_error("Signal error");
  return (old_action.sa_handler);
}

/*
 * sigquit_handler - SIGQUIT信号处理函数
 *
 * 功能：
 * 1. 处理SIGQUIT信号，优雅终止shell
 * 2. 打印终止信息并退出
 *
 * 参数：
 * sig - 信号编号
 */
void sigquit_handler(int sig) {
  printf("Terminating after receipt of SIGQUIT signal\n");
  exit(1);
}
