Crear un editor de texto de consola que permita agregar líneas, borrarlas y deshacer/rehacer operaciones usando pilas. Esta práctica ilustra cómo los editores mantienen bitácoras de cambios.
Comandos soportados:
ADD <texto>: agrega una línea al final.DEL: elimina la última línea.UNDO: revierte la última operación.REDO: reaplica la operación revertida.SHOW: imprime el documento.EXIT: cierra el editor.UNDO sobre un estado limpio no hace nada.REDO se limpia cuando llega un comando nuevo (distinto de REDO).strdup; recuerda liberar memoria en cada paso.#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef enum { ACC_ADD, ACC_DEL } AccionTipo;
typedef struct Nodo {
AccionTipo tipo;
char *linea;
struct Nodo *sig;
} Nodo;
typedef struct {
Nodo *top;
} PilaAccion;
void pila_init(PilaAccion *p) { p->top = NULL; }
int pila_empty(const PilaAccion *p) { return p->top == NULL; }
void pila_push(PilaAccion *p, AccionTipo tipo, const char *linea) {
Nodo *n = malloc(sizeof(Nodo));
n->tipo = tipo;
n->linea = linea ? strdup(linea) : NULL;
n->sig = p->top;
p->top = n;
}
char *pila_pop_linea(PilaAccion *p, AccionTipo *tipo) {
if (pila_empty(p)) return NULL;
Nodo *tmp = p->top;
p->top = tmp->sig;
if (tipo) *tipo = tmp->tipo;
char *linea = tmp->linea;
free(tmp);
return linea;
}
void pila_clear(PilaAccion *p) {
AccionTipo dummy;
while (!pila_empty(p)) free(pila_pop_linea(p, &dummy));
}
typedef struct {
char *lineas[256];
int total;
} Documento;
void doc_init(Documento *d) { d->total = 0; }
void doc_add(Documento *d, const char *texto) {
d->lineas[d->total++] = strdup(texto);
}
char *doc_del(Documento *d) {
if (d->total == 0) return NULL;
char *linea = d->lineas[--d->total];
d->lineas[d->total] = NULL;
return linea;
}
void doc_show(const Documento *d) {
puts("----- Documento -----");
for (int i = 0; i < d->total; ++i) {
printf("%d: %s\n", i + 1, d->lineas[i]);
}
}
void doc_clear(Documento *d) {
while (d->total) free(doc_del(d));
}
int main(void) {
Documento doc;
doc_init(&doc);
PilaAccion undo, redo;
pila_init(&undo);
pila_init(&redo);
char comando[16], buffer[256];
printf("Mini editor (ADD/D DEL UNDO REDO SHOW EXIT)\n");
while (1) {
printf("> ");
if (scanf("%15s", comando) != 1) break;
if (strcmp(comando, "EXIT") == 0) break;
if (strcmp(comando, "ADD") == 0) {
fgets(buffer, sizeof(buffer), stdin);
buffer[strcspn(buffer, "\n")] = '\0';
doc_add(&doc, buffer);
pila_push(&undo, ACC_ADD, buffer);
pila_clear(&redo);
} else if (strcmp(comando, "DEL") == 0) {
char *linea = doc_del(&doc);
if (linea) {
pila_push(&undo, ACC_DEL, linea);
pila_clear(&redo);
free(linea);
}
} else if (strcmp(comando, "SHOW") == 0) {
doc_show(&doc);
} else if (strcmp(comando, "UNDO") == 0) {
AccionTipo tipo;
char *linea = pila_pop_linea(&undo, &tipo);
if (!linea && tipo != ACC_DEL) {
puts("Nada que deshacer.");
} else {
if (tipo == ACC_ADD) {
char *borrada = doc_del(&doc);
if (borrada) free(borrada);
pila_push(&redo, ACC_ADD, linea);
} else if (tipo == ACC_DEL) {
doc_add(&doc, linea);
pila_push(&redo, ACC_DEL, linea);
}
free(linea);
}
} else if (strcmp(comando, "REDO") == 0) {
AccionTipo tipo;
char *linea = pila_pop_linea(&redo, &tipo);
if (!linea && tipo != ACC_DEL) {
puts("Nada que rehacer.");
} else {
if (tipo == ACC_ADD) {
doc_add(&doc, linea);
} else if (tipo == ACC_DEL) {
char *borrada = doc_del(&doc);
if (borrada) free(borrada);
}
pila_push(&undo, tipo, linea);
free(linea);
}
} else {
puts("Comando desconocido");
fgets(buffer, sizeof(buffer), stdin); // limpiar resto
}
}
doc_clear(&doc);
pila_clear(&undo);
pila_clear(&redo);
return 0;
}
INSERT <posición> y REPLACE con su correspondiente historial.