commit 8c14f40db70e59d9bea3e873fd57e334dbf0a969
Auteur: Selve <selve@asteride.xyz>
Date: Wed, 28 Feb 2024 15:34:54 -0500
naissance d'encore
Diffstat:
A | .gitignore | | | 3 | +++ |
A | Makefile | | | 19 | +++++++++++++++++++ |
A | cve.c | | | 15 | +++++++++++++++ |
A | cve.h | | | 6 | ++++++ |
A | encore.c | | | 504 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | paquet.c | | | 545 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | paquet.h | | | 53 | +++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | sm2.c | | | 62 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | sm2.h | | | 6 | ++++++ |
9 files changed, 1213 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,3 @@
+*.o
+*.core
+encore
diff --git a/Makefile b/Makefile
@@ -0,0 +1,19 @@
+COPT_SPCL = -O2 -DNDEBUG
+COPT = -Wall -Werror -Wextra -Wpedantic --std=c99 ${COPT_SPCL}
+CC = cc
+PREFIX = "/usr/local/bin"
+
+.SUFFIXES: .c .o
+.c.o:
+ ${CC} ${COPT} -c "$<"
+
+all: encore
+
+encore: encore.o paquet.o sm2.o cve.o
+ ${CC} ${COPT} -o "$@" encore.o paquet.o sm2.o cve.o
+
+install: encore
+ install -m 755 encore ${PREFIX}/encore
+
+clean:
+ rm -rf *.o *.core encore
diff --git a/cve.c b/cve.c
@@ -0,0 +1,15 @@
+#include "cve.h"
+
+int
+cve(char *s, unsigned int *n)
+{
+ int tmp;
+
+ for (tmp = 0; *s != '\0'; s++) {
+ if (*s < '0' || *s > '9')
+ return -1;
+ tmp = tmp * 10 + *s - '0';
+ }
+ *n = tmp;
+ return 0;
+}
diff --git a/cve.h b/cve.h
@@ -0,0 +1,6 @@
+#ifndef CVE_H
+#define CVE_H
+
+int cve(char *, unsigned int *);
+
+#endif
diff --git a/encore.c b/encore.c
@@ -0,0 +1,504 @@
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "paquet.h"
+#include "cve.h"
+
+#define CMD '\0' /* commande racine */
+#define CMD_Q 'q' /* questionner */
+#define CMD_R 'r' /* répondre */
+#define CMD_S 's' /* statistiquer */
+#define CMD_A 'a' /* ajouter */
+#define CMD_E 'e' /* enlever */
+#define CMD_I 'i' /* initiliser */
+#define CMD_M 'm' /* modifier */
+#define CMD_C 'c' /* commencer */
+
+static int cmd_q(int, char **);
+static int cmd_r(int, char **);
+static int cmd_s(int, char **);
+static int cmd_a(int, char **);
+static int cmd_e(int, char **);
+static int cmd_i(int, char **);
+static int cmd_m(int, char **);
+static int cmd_c(int, char **);
+
+static void utilisation(FILE *, int);
+
+char *nom_prog;
+
+int
+main(int argc, char **argv)
+{
+ char opt;
+ int (*cmd)(int, char **);
+
+ if ((nom_prog = argv[0]) == NULL || nom_prog[0] == '\0')
+ nom_prog = "encore";
+
+ opterr = 0;
+ while ((opt = getopt(argc, argv, "h")) != -1) {
+ switch (opt) {
+ case 'h':
+ utilisation(stdout, CMD);
+ return 0;
+ default:
+ goto err_util;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ optind = 1;
+ optreset = 1;
+
+ if (argc == 0)
+ goto err_util;
+
+ if (argv[0][1] != '\0')
+ goto err_util;
+
+ switch (argv[0][0]) {
+ case CMD_Q: cmd = &cmd_q; break;
+ case CMD_R: cmd = &cmd_r; break;
+ case CMD_S: cmd = &cmd_s; break;
+ case CMD_A: cmd = &cmd_a; break;
+ case CMD_E: cmd = &cmd_e; break;
+ case CMD_I: cmd = &cmd_i; break;
+ case CMD_M: cmd = &cmd_m; break;
+ case CMD_C: cmd = &cmd_c; break;
+ default: goto err_util;
+ }
+
+ return (*cmd)(argc, argv);
+
+err_util:
+ utilisation(stderr, CMD);
+ return 1;
+}
+
+static int
+cmd_q(int argc, char **argv)
+{
+ struct paquet pq;
+ int opts_prch;
+ char opt;
+ int id;
+
+ assert(argc >= 1);
+ assert(argv != NULL);
+
+ opts_prch = 0;
+
+ while ((opt = getopt(argc, argv, "NVh")) != -1) {
+ switch (opt) {
+ case 'N':
+ opts_prch |= PQ_OPT_PRCH_NOUV;
+ continue;
+ case 'V':
+ opts_prch |= PQ_OPT_PRCH_VIEU;
+ continue;
+ case 'h':
+ utilisation(stdout, CMD_Q);
+ return 0;
+ default:
+ goto err_util;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ goto err_util;
+
+ if (pq_ouvrir(argv[0], &pq) < 0)
+ return 1;
+
+ if ((id = pq_prochain(&pq, opts_prch)) < 0)
+ return 1;
+
+ printf("%u\n", id);
+
+ return 0;
+
+err_util:
+ utilisation(stderr, CMD_Q);
+ return 1;
+}
+
+static int
+cmd_r(int argc, char **argv)
+{
+ struct paquet pq;
+ char opt;
+ unsigned int qual;
+
+ assert(argc >= 1);
+ assert(argv != NULL);
+
+ qual = 4;
+
+ while ((opt = getopt(argc, argv, "q:h")) != -1) {
+ switch (opt) {
+ case 'q':
+ if (cve(optarg, &qual) < 0)
+ goto err_val;
+ if (qual < 0 || qual > 5)
+ goto err_val;
+ continue;
+ case 'h':
+ utilisation(stdout, CMD_R);
+ return 0;
+ default:
+ goto err_util;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ goto err_util;
+
+ if (pq_ouvrir(argv[0], &pq) < 0)
+ return 1;
+
+ if (pq_reponse(&pq, qual) < 0)
+ return 1;
+
+ return 0;
+
+err_util:
+ utilisation(stderr, CMD_R);
+ return 1;
+err_val:
+ utilisation(stderr, CMD_R);
+ fprintf(stderr, "%s n'est pas un entier entre 0 et 5\n", optarg);
+ return 1;
+}
+
+static int
+cmd_s(int argc, char **argv)
+{
+ struct paquet pq;
+ char opt;
+
+ assert(argc >= 1);
+ assert(argv != NULL);
+
+ while ((opt = getopt(argc, argv, "h")) != -1) {
+ switch (opt) {
+ case 'h':
+ utilisation(stdout, CMD_S);
+ return 0;
+ default:
+ goto err_util;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ goto err_util;
+
+ if (pq_ouvrir(argv[0], &pq) < 0)
+ return 1;
+
+ printf("version: %d\n"
+ "drapeaux: %d\n"
+ "début du cimetière: %u\n"
+ "début de l'incubateur: %u\n"
+ "nombre d'entrées: %u\n"
+ "pins: %d\n"
+ "pipi: %u\n"
+ "pnpi: %u\n"
+ "fi: %u\n"
+ "date: %llu\n"
+ "nfs: %u\n"
+ "nfsn: %u\n",
+ pq.entete.version,
+ pq.entete.draps,
+ pq.entete.deb_s,
+ pq.entete.deb_n,
+ pq.entete.nb,
+ pq.entete.pins,
+ pq.entete.pipi,
+ pq.entete.pnpi,
+ pq.entete.fi,
+ pq.entete.date << 8,
+ pq.entete.nfs,
+ pq.entete.nfsn);
+
+ if (pq_fiches_afficher(&pq) < 0)
+ return 1;
+
+ return 0;
+
+err_util:
+ utilisation(stderr, CMD_S);
+ return 1;
+}
+
+static int
+cmd_a(int argc, char **argv)
+{
+ struct paquet pq;
+ unsigned int n;
+ char opt;
+
+ assert(argc >= 1);
+ assert(argv != NULL);
+
+ n = 1;
+
+ while ((opt = getopt(argc, argv, "n:h")) != -1) {
+ switch (opt) {
+ case 'n':
+ if (cve(optarg, &n) < 0) {
+ utilisation(stderr, CMD_A);
+ fprintf(stderr,
+ "%s doit être un entier positif\n",
+ optarg);
+ return 0;
+ }
+ continue;
+ case 'h':
+ utilisation(stdout, CMD_A);
+ return 0;
+ default:
+ goto err_util;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ goto err_util;
+
+ if (pq_ouvrir(argv[0], &pq) < 0)
+ return 1;
+
+ if (pq_ajouter(&pq, n) < 0)
+ return 1;
+
+ return 0;
+
+err_util:
+ utilisation(stderr, CMD_A);
+ return 1;
+}
+
+static int
+cmd_e(int argc, char **argv)
+{
+ assert(argc >= 1);
+ assert(argv != NULL);
+ assert(argv[0] != NULL);
+
+ (void)argc;
+ (void)argv;
+
+ utilisation(stdout, CMD_E);
+
+ return 0;
+}
+
+static int
+cmd_i(int argc, char **argv)
+{
+ int opt;
+
+ assert(argc >= 1);
+ assert(argv != NULL);
+
+ while ((opt = getopt(argc, argv, "h")) != -1) {
+ switch (opt) {
+ case 'h':
+ utilisation(stdout, CMD_I);
+ return 0;
+ default:
+ goto err_util;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ goto err_util;
+
+ if (pq_init(argv[0]) < 0)
+ return 1;
+
+ return 0;
+
+err_util:
+ utilisation(stderr, CMD_I);
+ return 1;
+}
+
+static int
+cmd_m(int argc, char **argv)
+{
+ struct paquet pq;
+ unsigned int n;
+ char opt;
+ char *opts = "s:i:n:f:q:c:h";
+
+ assert(argc >= 0);
+ assert(argv != NULL);
+
+ while (getopt(argc, argv, opts) != -1)
+ ;
+
+ if (argc - optind == 0)
+ goto err_util;
+
+ if (pq_ouvrir(argv[optind], &pq) < 0)
+ return 1;
+
+ optind = 1;
+ optreset = 1;
+
+ while ((opt = getopt(argc, argv, opts)) != -1) {
+ switch (opt) {
+ case 's':
+ if (cve(optarg, &n) < 0 || n > (uint8_t) ~0)
+ goto invalide;
+ pq.entete.pins = n;
+ continue;
+ case 'i':
+ if (cve(optarg, &n) < 0 || n > (uint16_t) ~0)
+ goto invalide;
+ pq.entete.pipi = n;
+ continue;
+ case 'n':
+ if (cve(optarg, &n) < 0 || n > (uint16_t) ~0)
+ goto invalide;
+ pq.entete.pnpi = n;
+ continue;
+ case 'f':
+ if (cve(optarg, &n) < 0 || n > (uint8_t) ~0)
+ goto invalide;
+ pq.entete.fi = n;
+ continue;
+ case 'q':
+ if (cve(optarg, &n) < 0 || n > (uint8_t) ~0)
+ goto invalide;
+ pq.entete.nfsn = n;
+ continue;
+ case 'c':
+ if (optarg[1] != '\0')
+ goto err_util;
+ if (optarg[0] == '0')
+ pq.entete.draps &= ~PQ_DRAP_NOUV;
+ else if (optarg[0] == '1')
+ pq.entete.draps |= PQ_DRAP_NOUV;
+ continue;
+ case 'h':
+ utilisation(stdout, CMD_M);
+ return 0;
+ default:
+ goto err_util;
+ }
+ }
+
+ if (pq_entete_maj(&pq) < 0)
+ return 1;
+
+ return 0;
+
+invalide:
+ utilisation(stderr, CMD_M);
+ fprintf(stderr, "%s n'est pas un nombre valide\n", optarg);
+ return 1;
+err_util:
+ utilisation(stderr, CMD_M);
+ return 1;
+}
+
+static int
+cmd_c(int argc, char **argv)
+{
+ struct paquet pq;
+ char opt;
+ char *opts = "sh";
+
+ assert(argc >= 0);
+ assert(argv != NULL);
+
+ while (getopt(argc, argv, opts) != -1)
+ ;
+
+ if (argc - optind == 0)
+ goto err_util;
+
+ if (pq_ouvrir(argv[optind], &pq) < 0)
+ return 1;
+
+ optind = 1;
+ optreset = 1;
+ while ((opt = getopt(argc, argv, opts)) != -1) {
+ switch (opt) {
+ case 's':
+ pq.entete.nfs = pq.entete.nfsn;
+ continue;
+ case 'h':
+ utilisation(stdout, CMD_C);
+ return 0;
+ default:
+ goto err_util;
+ }
+ }
+
+ if (pq_entete_maj(&pq) < 0)
+ return 1;
+
+ return 0;
+
+err_util:
+ utilisation(stderr, CMD_C);
+ return 1;
+}
+
+static void
+utilisation(FILE *f, int cmd)
+{
+ char *util;
+ unsigned int i;
+ char *msgs[] = {
+ "[-h]",
+ "q [-hNV] paquet",
+ "r [-h] [-q qualité] paquet",
+ "s [-h] paquet",
+ "a [-h] [-n nb] paquet",
+ "e ??? paquet",
+ "i [-h] paquet",
+ ("m [-h] [-s pins] [-i pipi] [-n pnpi] [-f fi] [-q nfsn] "
+ "[-c dn] paquet"),
+ "c [-h] ??? paquet" };
+
+ assert(f != NULL);
+
+ util = "Utilisation:";
+ fprintf(f, "%s", util);
+
+ switch (cmd) {
+ case CMD:
+ fputc('\n', f);
+ for (i = 0; i < sizeof(msgs) / sizeof(char *); i++)
+ fprintf(f, " %s %s\n", nom_prog, msgs[i]);
+ return;
+ case CMD_Q: i = 1; break;
+ case CMD_R: i = 2; break;
+ case CMD_S: i = 3; break;
+ case CMD_A: i = 4; break;
+ case CMD_E: i = 5; break;
+ case CMD_I: i = 6; break;
+ case CMD_M: i = 7; break;
+ case CMD_C: i = 8; break;
+ default: assert(0);
+ }
+
+ fprintf(f, " %s %s\n", nom_prog, msgs[i]);
+}
diff --git a/paquet.c b/paquet.c
@@ -0,0 +1,545 @@
+#include <assert.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "paquet.h"
+#include "sm2.h"
+
+#define PQ_VERSION 0
+
+#define PQ_ENTETE_LG 28
+#define PQ_FICHE_LG 12
+
+#define DEC_ENTETE_VERSION 0
+#define DEC_ENTETE_DRAPS 1
+#define DEC_ENTETE_DEB_S 2
+#define DEC_ENTETE_DEB_N 5
+#define DEC_ENTETE_NB 8
+#define DEC_ENTETE_PINS 11
+#define DEC_ENTETE_PIPI 12
+#define DEC_ENTETE_PNPI 14
+#define DEC_ENTETE_FI 16
+#define DEC_ENTETE_DATE 17
+#define DEC_ENTETE_NFS 24
+#define DEC_ENTETE_NFSN 26
+
+#define DEC_FICHE_PRCH 0
+#define DEC_FICHE_DERN 3
+#define DEC_FICHE_ID 6
+#define DEC_FICHE_FAC 9
+#define DEC_FICHE_NSUCC 10
+
+#define PQ_PINS_DEF 3
+#define PQ_PIPI_DEF 1
+#define PQ_PNPI_DEF 377
+#define PQ_FI_DEF 180
+#define PQ_NFSN_DEF 20
+
+#define TAMPON_TLL (4096 - (4096 % PQ_FICHE_LG))
+#define TAMPON_FICHES (TAMPON_TLL / PQ_FICHE_LG)
+
+#define LIRE_1(tp) \
+ (((uint8_t) *(tp)))
+#define LIRE_2(tp) \
+ (((uint16_t) *(tp) << 8) \
+ + ((uint16_t) *((tp) + 1)))
+#define LIRE_3(tp) \
+ (((uint32_t) *(tp) << 16) \
+ + ((uint32_t) *((tp) + 1) << 8) \
+ + ((uint32_t) *((tp) + 2)))
+#define LIRE_7(tp) \
+ (((uint64_t) *(tp) << 48) \
+ + ((uint64_t) *((tp) + 1) << 40) \
+ + ((uint64_t) *((tp) + 2) << 32) \
+ + ((uint64_t) *((tp) + 3) << 24) \
+ + ((uint64_t) *((tp) + 4) << 16) \
+ + ((uint64_t) *((tp) + 5) << 8) \
+ + ((uint64_t) *((tp) + 6)))
+
+#define PARENT(pos) ((pos) / 2)
+#define ENFANT1(pos) ((pos) * 2 + 1)
+#define ENFANT2(pos) ((pos) * 2 + 2)
+
+static int pq_fiche_copier(struct paquet *, uint32_t, uint8_t *);
+static int pq_fiche_coller(struct paquet *, uint32_t, uint8_t *);
+static int pq_fiche_monter(struct paquet *, uint32_t, uint8_t *);
+static int pq_fiche_descendre(struct paquet *, uint32_t, uint8_t *);
+
+extern char *nom_prog;
+
+/* Créer la base de données au chemin et écrire les valeurs par défaut.
+ * Ne créer la base de données que s'il n'y a pas de paquet à chemin */
+int
+pq_init(char *chemin)
+{
+ struct paquet pq;
+
+ assert(chemin != NULL);
+ assert(PQ_ENTETE_LG + 1 < (1 << 8));
+
+ if ((pq.fd = open(chemin, O_WRONLY | O_CREAT | O_EXCL,
+ S_IRUSR | S_IWUSR)) < 0) {
+ perror(chemin);
+ return -1;
+ }
+
+ pq.nom = chemin;
+ pq.entete.version = 0;
+ pq.entete.draps = 0;
+ pq.entete.deb_s = 0;
+ pq.entete.deb_n = 0;
+ pq.entete.nb = 0;
+ pq.entete.pins = PQ_PINS_DEF;
+ pq.entete.pipi = PQ_PIPI_DEF;
+ pq.entete.pnpi = PQ_PNPI_DEF;
+ pq.entete.fi = PQ_FI_DEF;
+ pq.entete.date = (uint64_t) time(NULL) >> 8;
+ pq.entete.nfs = 0;
+ pq.entete.nfsn = PQ_NFSN_DEF - 20;
+
+ if (pq_entete_maj(&pq) < 0)
+ return -1;
+
+ if (close(pq.fd) < 0) {
+ perror(chemin);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* ouvrir le paquet pointé par chemin, en lire l'entête et remplir un struct
+ * paquet */
+int
+pq_ouvrir(char *chemin, struct paquet *pq)
+{
+ uint8_t tp[PQ_ENTETE_LG];
+
+ assert(chemin != NULL);
+ assert(pq != NULL);
+
+ if ((pq->fd = open(chemin, O_RDWR)) < 0) {
+ perror(chemin);
+ return -1;
+ }
+
+ if (read(pq->fd, tp, PQ_ENTETE_LG) != PQ_ENTETE_LG) {
+ fprintf(stderr, "%s: paquet corrompu\n", chemin);
+ return -1;
+ }
+
+ pq->nom = chemin;
+
+ if ((pq->entete.version = LIRE_1(tp + DEC_ENTETE_VERSION)) != 0) {
+ fprintf(stderr, "%d: version incompatible\n",
+ pq->entete.version);
+ return -1;
+ }
+
+ pq->entete.draps = LIRE_1(tp + DEC_ENTETE_DRAPS);
+ pq->entete.deb_s = LIRE_3(tp + DEC_ENTETE_DEB_S);
+ pq->entete.deb_n = LIRE_3(tp + DEC_ENTETE_DEB_N);
+ pq->entete.nb = LIRE_3(tp + DEC_ENTETE_NB);
+ pq->entete.pins = LIRE_1(tp + DEC_ENTETE_PINS);
+ pq->entete.pipi = LIRE_2(tp + DEC_ENTETE_PIPI);
+ pq->entete.pnpi = LIRE_2(tp + DEC_ENTETE_PNPI);
+ pq->entete.fi = LIRE_1(tp + DEC_ENTETE_FI);
+ pq->entete.date = LIRE_7(tp + DEC_ENTETE_DATE);
+ pq->entete.nfs = LIRE_2(tp + DEC_ENTETE_NFS);
+ pq->entete.nfsn = LIRE_2(tp + DEC_ENTETE_NFSN);
+
+ return 0;
+}
+
+/* ajouter n entrées à la base de données
+ * les entrées sont ajoutées dans la section « nouveaux » */
+int
+pq_ajouter(struct paquet *pq, unsigned int n)
+{
+ uint32_t id;
+ uint8_t tp[TAMPON_TLL] = { 0 };
+ uint8_t *ptr;
+ int min;
+ int reste;
+
+ assert(pq != NULL);
+
+ id = pq->entete.nb ;
+
+ if (lseek(pq->fd, PQ_ENTETE_LG + id * PQ_FICHE_LG, SEEK_SET) < 0) {
+ perror(pq->nom);
+ return -1;
+ }
+
+ while (n != 0) {
+ assert(n > 0);
+ reste = min = n < TAMPON_FICHES ? n : TAMPON_FICHES;
+ for (ptr = tp; reste > 0; reste--, ptr += PQ_FICHE_LG) {
+ ptr[6] = (uint8_t) (id >> 16);
+ ptr[7] = (uint8_t) (id >> 8);
+ ptr[8] = (uint8_t) (id);
+ ptr[9] = (uint8_t) (pq->entete.fi);
+ id++;
+ }
+ reste = min * PQ_FICHE_LG;
+ if (write(pq->fd, tp, reste) != reste) {
+ perror(pq->nom);
+ return -1;
+ }
+ pq->entete.nb += min;
+ n -= min;
+ }
+
+ if (pq_entete_maj(pq) < 0)
+ return -1;
+
+ return 0;
+}
+
+/* retourner l'identifiant de la prochaine fiche à réviser */
+int
+pq_prochain(struct paquet *pq, int opts)
+{
+ uint8_t fiche[PQ_FICHE_LG];
+ uint32_t indice;
+
+ assert(pq != NULL);
+
+ if (pq->entete.draps & PQ_DRAP_QST) {
+ if (pq->entete.draps & PQ_DRAP_MNC)
+ indice = 0;
+ else
+ indice = pq->entete.deb_n;
+ } else {
+ pq->entete.draps |= PQ_DRAP_QST;
+
+ if (pq->entete.nb - pq->entete.deb_n > 0
+ && (pq->entete.nfs > 0
+ || !(pq->entete.draps & PQ_DRAP_NOUV)
+ || opts & PQ_OPT_PRCH_NOUV)
+ && !(opts & PQ_OPT_PRCH_VIEU)) {
+ pq->entete.draps &= ~PQ_DRAP_MNC;
+ indice = pq->entete.deb_n;
+ } else if (pq->entete.deb_s > 0) {
+ pq->entete.draps |= PQ_DRAP_MNC;
+ indice = 0;
+ } else {
+ fprintf(stderr,
+ "%s: le paquet %s ne contient aucune fiche\n",
+ nom_prog, pq->nom);
+ return -1;
+ }
+
+ if (pq_entete_maj(pq) < 0)
+ return -1;
+ }
+
+ if (pq_fiche_copier(pq, indice, fiche) < 0)
+ return -1;
+
+ if (((uint64_t) time(NULL) >> 8)
+ < (LIRE_3(fiche + DEC_FICHE_PRCH) + pq->entete.date)) {
+ fprintf(stderr, "%s: aucune fiche à réviser\n", nom_prog);
+ return -1;
+ }
+
+ return LIRE_3(fiche + DEC_FICHE_ID);
+}
+
+int
+pq_reponse(struct paquet *pq, int rep)
+{
+ struct fiche f;
+ int pos;
+ uint8_t fiche[PQ_FICHE_LG];
+
+ assert(rep >= 0 && rep <= 5);
+
+ if (!(pq->entete.draps & PQ_DRAP_QST)) {
+ fprintf(stderr, "%s: veuillez d'abord exécuter la commande q\n",
+ nom_prog);
+ return -1;
+ }
+
+ if (pq->entete.draps & PQ_DRAP_MNC)
+ pos = 0;
+ else
+ pos = pq->entete.deb_n;
+
+ pq->entete.draps &= ~PQ_DRAP_QST;
+
+ if (pq_fiche_copier(pq, pos, fiche) < 0)
+ return -1;
+
+ f.prch = LIRE_3(fiche + DEC_FICHE_PRCH);
+ f.dern = LIRE_3(fiche + DEC_FICHE_DERN);
+ f.id = LIRE_3(fiche + DEC_FICHE_ID);
+ f.fac = LIRE_1(fiche + DEC_FICHE_FAC);
+ f.nsucc = LIRE_2(fiche + DEC_FICHE_NSUCC);
+
+ sm2(&f, rep, pq->entete.pins, pq->entete.pipi, pq->entete.pnpi, pq->entete.date);
+
+ fiche[0] = (uint8_t) (f.prch >> 16);
+ fiche[1] = (uint8_t) (f.prch >> 8);
+ fiche[2] = (uint8_t) (f.prch);
+ fiche[3] = (uint8_t) (f.dern >> 16);
+ fiche[4] = (uint8_t) (f.dern >> 8);
+ fiche[5] = (uint8_t) (f.dern);
+ fiche[6] = (uint8_t) (f.id >> 16);
+ fiche[7] = (uint8_t) (f.id >> 8);
+ fiche[8] = (uint8_t) (f.id);
+ fiche[9] = (uint8_t) (f.fac);
+ fiche[10] = (uint8_t) (f.nsucc >> 8);
+ fiche[11] = (uint8_t) (f.nsucc);
+
+ if (pos == 0 && pq->entete.deb_s > 0) {
+ if (pq_fiche_coller(pq, 0, fiche) < 0)
+ return -1;
+ if (pq_fiche_descendre(pq, 0, fiche) < 0)
+ return -1;
+ } else {
+ if (pq->entete.deb_n != pq->entete.deb_s) {
+ uint8_t fiche2[PQ_FICHE_LG];
+ if (pq_fiche_copier(pq, pq->entete.deb_s, fiche2) < 0)
+ return -1;
+ if (pq_fiche_coller(pq, pq->entete.deb_n, fiche2) < 0)
+ return -1;
+ }
+ if (pq_fiche_coller(pq, pq->entete.deb_s, fiche) < 0)
+ return -1;
+ pq->entete.deb_s++;
+ pq->entete.deb_n++;
+ if (pq->entete.draps & PQ_DRAP_NOUV)
+ pq->entete.nfs--;
+ assert(pq->entete.nfs >= 0);
+ if (pq_fiche_monter(pq, pos, fiche) < 0)
+ return -1;
+ }
+
+ if (pq_entete_maj(pq) < 0)
+ return -1;
+
+ return 0;
+}
+
+int
+pq_entete_maj(struct paquet *pq)
+{
+ uint8_t texte[] = {
+ pq->entete.version,
+ pq->entete.draps,
+ (uint8_t) (pq->entete.deb_s >> 16),
+ (uint8_t) (pq->entete.deb_s >> 8),
+ (uint8_t) (pq->entete.deb_s),
+ (uint8_t) (pq->entete.deb_n >> 16),
+ (uint8_t) (pq->entete.deb_n >> 8),
+ (uint8_t) (pq->entete.deb_n),
+ (uint8_t) (pq->entete.nb >> 16),
+ (uint8_t) (pq->entete.nb >> 8),
+ (uint8_t) (pq->entete.nb),
+ pq->entete.pins,
+ (uint8_t) (pq->entete.pipi >> 8),
+ (uint8_t) (pq->entete.pipi),
+ (uint8_t) (pq->entete.pnpi >> 8),
+ (uint8_t) (pq->entete.pnpi),
+ pq->entete.fi,
+ (uint8_t) (pq->entete.date >> 48),
+ (uint8_t) (pq->entete.date >> 40),
+ (uint8_t) (pq->entete.date >> 32),
+ (uint8_t) (pq->entete.date >> 24),
+ (uint8_t) (pq->entete.date >> 16),
+ (uint8_t) (pq->entete.date >> 8),
+ (uint8_t) (pq->entete.date),
+ (uint8_t) (pq->entete.nfs >> 8),
+ (uint8_t) (pq->entete.nfs),
+ (uint8_t) (pq->entete.nfsn >> 8),
+ (uint8_t) (pq->entete.nfsn) };
+
+ assert(pq != NULL);
+ assert(sizeof(texte) == PQ_ENTETE_LG);
+
+ if (lseek(pq->fd, 0, SEEK_SET) < 0) {
+ perror(pq->nom);
+ return -1;
+ }
+
+ if (write(pq->fd, texte, PQ_ENTETE_LG) != PQ_ENTETE_LG) {
+ perror(pq->nom);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+pq_fiche_copier(struct paquet *pq, uint32_t pos, uint8_t *tp)
+{
+ if (lseek(pq->fd, PQ_ENTETE_LG + pos * PQ_FICHE_LG, SEEK_SET) < 0) {
+ perror(pq->nom);
+ return -1;
+ }
+
+ if (read(pq->fd, tp, PQ_FICHE_LG) != PQ_FICHE_LG) {
+ perror(pq->nom);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+pq_fiche_coller(struct paquet *pq, uint32_t pos, uint8_t *tp)
+{
+ if (lseek(pq->fd, PQ_ENTETE_LG + pos * PQ_FICHE_LG, SEEK_SET) < 0) {
+ perror(pq->nom);
+ }
+
+ if (write(pq->fd, tp, PQ_FICHE_LG) != PQ_FICHE_LG) {
+ perror(pq->nom);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* faire monter une fiche dans le monceau */
+static int
+pq_fiche_monter(struct paquet *pq, uint32_t pos, uint8_t *tp)
+{
+ uint8_t tp_parent[PQ_FICHE_LG];
+ uint32_t date;
+ uint32_t pos_orig;
+
+ assert(pq != NULL);
+ assert(tp != NULL);
+ assert(pq->entete.deb_s > pos); /* l'entrée doit être dans le monceau */
+
+ date = LIRE_3(tp + DEC_FICHE_PRCH);
+ for (pos_orig = pos; pos != 0; pos = PARENT(pos)) {
+ /* copier le parent */
+ if (pq_fiche_copier(pq, PARENT(pos), tp_parent) < 0)
+ return -1;
+
+ if (date >= LIRE_3(tp_parent + DEC_FICHE_PRCH))
+ break;
+
+ /* descendre le parent */
+ if (pq_fiche_coller(pq, pos, tp_parent) < 0)
+ return -1;
+ }
+
+ /* ne coller l'entrée que si l'enfant doit être déplacé */
+ if (pos != pos_orig)
+ if (pq_fiche_coller(pq, pos, tp) < 0)
+ return -1;
+
+ return 0;
+}
+
+/* faire descendre une entrée dans le monceau */
+static int
+pq_fiche_descendre(struct paquet *pq, uint32_t pos, uint8_t *tp)
+{
+ uint8_t tp_enfants[PQ_FICHE_LG * 2];
+ uint32_t date_parent;
+ uint32_t date_enfants[2];
+ int inf; /* sert à sélectionner l'enfant inférieur */
+ uint32_t pos_orig;
+
+ assert(pq != NULL);
+ assert(pq->entete.deb_s > pos); /* l'entrée doit être dans le monceau */
+
+ date_parent = LIRE_3(tp + DEC_FICHE_PRCH);
+ for (pos_orig = pos; ENFANT1(pos) < pq->entete.deb_s; pos = ENFANT1(pos) + inf) {
+ if (pq_fiche_copier(pq, ENFANT1(pos), tp_enfants) < 0)
+ return -1;
+
+ date_enfants[0] = LIRE_3(tp_enfants + DEC_FICHE_PRCH);
+ inf = 0;
+
+ if (ENFANT2(pos) < pq->entete.deb_s) {
+ if (pq_fiche_copier(pq, ENFANT2(pos),
+ tp_enfants + PQ_FICHE_LG) < 0)
+ return -1;
+ date_enfants[1] = LIRE_3(tp_enfants
+ + PQ_FICHE_LG
+ + DEC_FICHE_PRCH);
+ if (date_enfants[1] < date_enfants[0]) {
+ date_enfants[0] = date_enfants[1];
+ inf = 1;
+ }
+ }
+
+ /* monter l'enfant */
+ if (date_enfants[inf] <= date_parent) {
+ if (pq_fiche_coller(pq, pos, tp_enfants
+ + PQ_FICHE_LG * inf) < 0)
+ return -1;
+ } else {
+ break;
+ }
+ }
+
+
+ /* ne coller l'entrée que si le parent doit être déplacé */
+ if (pos != pos_orig)
+ if (pq_fiche_coller(pq, pos, tp) < 0)
+ return -1;
+
+ return 0;
+}
+
+int
+pq_fiches_afficher(struct paquet *pq)
+{
+ uint8_t *tp;
+ int n;
+ int ncar;
+
+ ncar = pq->entete.nb * PQ_FICHE_LG;
+
+ if ((tp = malloc(ncar)) == NULL) {
+ perror(NULL);
+ return -1;
+ }
+
+ if (read(pq->fd, tp, ncar) != ncar) {
+ perror(pq->nom);
+ return -1;
+ }
+
+ printf("# MONCEAU\n");
+ for (n = pq->entete.deb_s; n > 0; n--, tp += PQ_FICHE_LG)
+ printf("%u\t%u\t%u\t%u\t%u\n",
+ LIRE_3(tp + DEC_FICHE_PRCH),
+ LIRE_3(tp + DEC_FICHE_DERN),
+ LIRE_3(tp + DEC_FICHE_ID),
+ LIRE_1(tp + DEC_FICHE_FAC),
+ LIRE_2(tp + DEC_FICHE_NSUCC));
+
+ printf("# CIMETIÈRE\n");
+ for (n = pq->entete.deb_n - pq->entete.deb_s; n > 0; n--, tp += PQ_FICHE_LG)
+ printf("%u\t%u\t%u\t%u\t%u\n",
+ LIRE_3(tp + DEC_FICHE_PRCH),
+ LIRE_3(tp + DEC_FICHE_DERN),
+ LIRE_3(tp + DEC_FICHE_ID),
+ LIRE_1(tp + DEC_FICHE_FAC),
+ LIRE_2(tp + DEC_FICHE_NSUCC));
+
+ printf("# NOUVEAUX\n");
+ for (n = pq->entete.nb - pq->entete.deb_n; n > 0; n--, tp += PQ_FICHE_LG)
+ printf("%u\t%u\t%u\t%u\t%u\n",
+ LIRE_3(tp + DEC_FICHE_PRCH),
+ LIRE_3(tp + DEC_FICHE_DERN),
+ LIRE_3(tp + DEC_FICHE_ID),
+ LIRE_1(tp + DEC_FICHE_FAC),
+ LIRE_2(tp + DEC_FICHE_NSUCC));
+
+ return 0;
+}
diff --git a/paquet.h b/paquet.h
@@ -0,0 +1,53 @@
+#ifndef PAQUET_H
+#define PAQUET_H
+
+#include <stdint.h>
+
+#define PQ_OPT_PRCH_VIEU 01 /* doit être un vieux, a préséance */
+#define PQ_OPT_PRCH_NOUV 02 /* peut être un nouveau */
+
+#define PQ_DRAP_NOUV 01 /* prendre en compte nfs */
+#define PQ_DRAP_QST 02 /* si on a posé une question */
+#define PQ_DRAP_MNC 04 /* si la nouvelle question se trouve dans le monceau */
+
+#define PQ_PHASE_N (1 << 15)
+
+struct entete {
+ uint8_t version;
+ uint8_t draps;
+ uint32_t deb_s;
+ uint32_t deb_n;
+ uint32_t nb;
+ uint8_t pins;
+ uint16_t pipi;
+ uint16_t pnpi;
+ uint8_t fi;
+ uint64_t date;
+ uint16_t nfs;
+ uint16_t nfsn;
+};
+
+struct paquet {
+ int fd;
+ char *nom;
+ struct entete entete;
+};
+
+struct fiche {
+ uint32_t prch;
+ uint32_t dern;
+ uint32_t id;
+ uint8_t fac;
+ uint16_t nsucc;
+};
+
+int pq_init(char *);
+int pq_ouvrir(char *, struct paquet *);
+int pq_ajouter(struct paquet *, unsigned int);
+int pq_prochain(struct paquet *, int);
+int pq_reponse(struct paquet *, int);
+int pq_entete_maj(struct paquet *);
+
+int pq_fiches_afficher(struct paquet *);
+
+#endif
diff --git a/sm2.c b/sm2.c
@@ -0,0 +1,62 @@
+#include <stdint.h>
+#include <time.h>
+
+#include <stdio.h>
+
+#include "paquet.h"
+#include "sm2.h"
+
+#define FMIN 1.3
+
+static uint8_t sm2_fac(int, int);
+
+void
+sm2(struct fiche *f, unsigned int qual, unsigned int pins, unsigned int pipi, unsigned int pnpi, uint64_t date) {
+ int phase;
+ unsigned int nsucc;
+ uint32_t intrv;
+ uint64_t mtn;
+
+ nsucc = f->nsucc & ~PQ_PHASE_N;
+ phase = (f->nsucc & PQ_PHASE_N) > 0;
+
+ f->fac = sm2_fac(f->fac, qual);
+
+ if (qual >= 4 && nsucc < PQ_PHASE_N - 1)
+ nsucc++;
+ else
+ nsucc = 0;
+
+ if (nsucc <= 1) {
+ if (phase == 0)
+ intrv = pipi;
+ else
+ intrv = pnpi;
+ } else if (phase == 0 && nsucc == pins + 1) {
+ phase = 1;
+ intrv = pnpi;
+ } else {
+ f->fac = sm2_fac(f->fac, qual);
+ intrv = (f->prch - f->dern) * (f->fac / 100.0 + FMIN) + 1;
+ }
+
+ mtn = (uint64_t) time(NULL) >> 8;
+
+ f->dern = mtn - date;
+ f->prch = f->dern + intrv;
+
+ f->nsucc = nsucc | (phase * PQ_PHASE_N);
+}
+
+static uint8_t
+sm2_fac(int fac, int qual)
+{
+ fac = fac - 80 + 28 * qual - 2 * qual * qual;
+
+ if (fac > 255)
+ fac = 255;
+ if (fac < 0)
+ fac = 0;
+
+ return fac;
+}
diff --git a/sm2.h b/sm2.h
@@ -0,0 +1,6 @@
+#ifndef SM2_H
+#define SM2_H
+
+void sm2(struct fiche *f, unsigned int qual, unsigned int pins, unsigned int pipi, unsigned int pnpi, uint64_t date);
+
+#endif