pre-postum: il est tard, j'ai la flemme de tout relire pour vérifier la cohérence une nième fois. Bref, attendez-vous à un contenu mal écrit et redondant.
TL;DR:
Et imaginons qu'on veuille avoir un lien sur tous les titres ? Pourquoi devoir les faire à la main quand on pourrait les générer à la main ? Idem, si on avoir un bête index.
En shell POSIX standard, à ma connaissance, c'est impossible. Alors j'ai écris un outil en C++ "light" destiné à remplir la fonctionnalité manquante.
L'outil est pas packagé, le code est publié ici pour la 1ère fois, à la base je voulais le faire sur le blog que je compte monter, mais le CSS m'écoeure alors je galère...
Le code source suit (amis codeurs, ne regardez pas: c'est sale):
#include<stdlib.h>#include<stdio.h>#include<errno.h>#include<string.h>#include<assert.h>#include<stdint.h>#include<ctype.h>#include<algorithm>#include<vector>/** * This program reads stdin and when consecutive lines have specific fields all * containing the same value, prints them replacing the newline character by * the 1st character in FIELD_SEP. * Fields are delimited by the FIELD_SEP environment variable. If not defined, * " \\t" is used instead (see isblank(3)). * Fields to use are defined by the environment variable FIELDS, which only * use unsigned decimal integers separated by commas, other characters makes the * value invalid. * If FIELDS is not defined or invalid, exits with an error. * Empty field indexes ("1,,3") are ignored (will resolve in "1,3"). * Do not work if input is not in line mode. * Line separator is defined by ENTRY_SEP, or "\\n" if not defined. * * TODO: * * check that ENTRY_SEP works as expected; * * fix the fact input needs a "\\n" at end of last line for it to be merged; * * UTF-8 support (field separators); * * providing FIELDS variable as command-line option; * * -v/--version option; * * -h/--help option; * * remove bloated STL containers; * * allow to customize the memory allocation scheme at runtime; * * allow to not print twice merged fields; * * allow to set verbosity on stderr; * * remove hard-coded limit of UINT16_MAX - 1 for fields start/stop positions; * * print as many lines as there where duplicates? * * Coding rules: * * const affect what is before it, so it must follow the type; **/classfield_marker{uint16_tm_start=UINT16_MAX,m_end=UINT16_MAX;public:boolignore(void)const{returnm_start==m_end&&m_start==UINT16_MAX;}voiddefine(uint16_tstart,uint16_tend){assert(end>=start&&start!=UINT8_MAX&&end!=UINT8_MAX);m_start=start;m_end=end;}uint16_tstart(void)const{assert(!ignore());returnm_start;}uint16_tend(void)const{assert(!ignore());returnm_end;}};boolallocate_markers(charconst*constFIELDS,std::vector<field_marker>&field_cache);intmain(void){charconst*constDEFAULT_FIELD_SEP=" \t";charconst*constDEFAULT_ENTRY_SEP="\n";charconst*SEP_START=getenv("FIELD_SEP");if(!SEP_START){SEP_START=DEFAULT_FIELD_SEP;}charconst*SEP_ENTRY=getenv("ENTRY_SEP");if(!SEP_ENTRY){SEP_ENTRY=DEFAULT_ENTRY_SEP;}charconst*constFIELDS=getenv("FIELDS");if(!FIELDS){fputs("ERROR: FIELDS is not defined\n",stderr);returnEXIT_FAILURE;}if(strlen(FIELDS)==0){fputs("ERROR: FIELDS is empty\n",stderr);returnEXIT_FAILURE;}std::vector<field_marker>field_cache;if(allocate_markers(FIELDS,field_cache)){returnEXIT_FAILURE;}size_tbuf_sz=2048;char*buf=nullptr;// I don't see how merging lines smaller than 16 bytes can be useful// also, not even enough mem for that would indicate bigger problems...while(!buf&&buf_sz>=32){buf_sz/=2;buf=static_cast<char*>(malloc(buf_sz));}if(!buf){fprintf(stderr,"ERROR: malloc %s(%d)\n",strerror(errno),errno);returnEXIT_FAILURE;}boolfetch=true;typedefstd::vector<char>line_cache;line_cachelast_line;charconst*constSEP_END=SEP_START+strlen(SEP_START);while(!feof(stdin)){if(!fgets(buf,static_cast<int>(buf_sz),stdin)){free(buf);buf=nullptr;buf_sz=0;if(!feof(stdin)){fprintf(stderr,"ERROR: fgets %s(%d)\n",strerror(errno),errno);returnEXIT_FAILURE;}break;}size_tstr_sz=strlen(buf);if(str_sz==buf_sz-1&&buf[str_sz]!='\n'&&!feof(stdin)){fprintf(stderr,"ERROR: buffer too small for some lines\n");returnEXIT_FAILURE;}if(!fetch){charconst*dst_ptr=buf;for(size_ti=0;i<field_cache.size();++i){field_markerconst&src=field_cache[i];if(src.ignore()){charconst*sep=SEP_END;while(sep==SEP_END){++dst_ptr;sep=SEP_START;for(;sep!=SEP_END&&*dst_ptr&&*dst_ptr!=*sep;++sep){}}++dst_ptr;continue;}charconst*src_ptr=last_line.data()+src.start();size_tlen=src.end()-src.start();if(len>buf_sz-static_cast<size_t>(dst_ptr-buf)){fetch=true;break;}if(0!=memcmp(dst_ptr,src_ptr,len)){fetch=true;break;}charlast=dst_ptr[len];charconst*sep_=SEP_START;assert(sep_!=nullptr);while(0!=*sep_&&*sep_!=last&&*SEP_ENTRY!=last){++sep_;}if(0==*sep_){fetch=true;break;}dst_ptr+=len;assert(dst_ptr>=buf);}fputc(fetch?*SEP_ENTRY:*SEP_START,stdout);}last_line.assign(buf,buf+str_sz);if(last_line.back()==*SEP_ENTRY){last_line.back()=0;}if(fetch){line_cache::iteratorstart=last_line.begin();line_cache::iteratorcache_end=last_line.end();size_tfield_index=0;while(start!=cache_end&&field_index<field_cache.size()){autoend=last_line.end();if(last_line.back()==0){--end;}line_cache::iteratorit=std::find_first_of(start,end,SEP_START,SEP_END);if(!field_cache[field_index].ignore()){field_cache[field_index].define(static_cast<uint16_t>(start-last_line.begin()),static_cast<uint16_t>(it-last_line.begin()));}start=it+1;++field_index;}fetch=false;}fputs(last_line.data(),stdout);}fputc(*SEP_ENTRY,stdout);returnEXIT_SUCCESS;}boolallocate_markers(charconst*constFIELDS,std::vector<field_marker>&field_cache){field_cache.reserve(UINT8_MAX);//255 fields should fit most casessize_tlast_field=0;charconst*fields=FIELDS-1;do{++fields;if(isdigit(*fields)){last_field=last_field*10+static_cast<size_t>(*fields-'0');}elseif(*fields==','||*fields==0){size_tmax=std::max(field_cache.size(),last_field);field_cache.resize(max);field_cache[last_field-1].define(0,0);last_field=0;}else{fputs("ERROR: FIELDS contains illegal characters\n",stderr);returntrue;}}while(*fields);field_cache.shrink_to_fit();returnfalse;}
C'est pas génial, je l'ai pas mis sur une forge, et pour la licence, bah... CC-0, BSD-2, WTFPL ou toute autre compatible, au choix. De toute façon ce code est tellement simple que je vois pas comment on pourrait y coller une licence en étant honnête...
Les lecteurs y trouverons des opinions techniques douteuses. Sincèrement: elles sont miennes, je veux bien en discuter, mais je souhaite généralement prouver que C++ peut être aussi léger que le C, avec des avantages nets en terme de code. Ce qui explique mon manque d'appréciation pour, notamment, la STL. Mais bref. Ce code est sale de toute façon, je le sais.
De base, mon idée était de faire un générateur de blog en shell, mais j'ai trouvé aucun outil permettant de merger des lignes en fonction d'une clé, d'où le code précédent.
Bref, j'ai codé cette fonction, elle marche avec mes scripts, et permets bien plus que faire un vulgaire blog: vu qu'elle fait quasi rien, des gens lui trouverons peut-être d'autres usages. Grand bien leur en fasse.
Mon idée était d'utiliser les outils UNIX classiques: sed, awk, cat... afin de passer un fichier source au travers d'une moulinette (par exemple /usr/bin/markdown du paquet discount.deb) et de concaténer ces fichiers avec un header, un footer, etc... mais, ça ne permets pas de générer des index, sommaires ou autres, alors j'ai sorti mon clang++.
Du coup, cet outil est quasiment un pré-compilateur: si ma mémoire est bonne, je récupère une liste de noms de champs de la 1ère ligne, considère que ces champs seront présents à la suite, et les utilise pour générer un texte greppable.
Une fois le texte greppable, je peux revenir au shell. La principale raison pour laquelle je n'ai rien publié est que... bah, je suis une merde en CSS, au point que je meurs d'envie de faire un <table><line><bla><bla/><line/><table/> dans l'idée... bref, le code est pas assez testé, il est sale, il est largement améliorable, et je le mentionne juste parce que, je me dis que p'tet, quelqu'un le trouveras utile...
[^] # Re: Jamstack ?
Posté par freem . En réponse au journal Générateur de site web statique. Évalué à 2.
pre-postum: il est tard, j'ai la flemme de tout relire pour vérifier la cohérence une nième fois. Bref, attendez-vous à un contenu mal écrit et redondant.
TL;DR:
En shell POSIX standard, à ma connaissance, c'est impossible. Alors j'ai écris un outil en C++ "light" destiné à remplir la fonctionnalité manquante.
L'outil est pas packagé, le code est publié ici pour la 1ère fois, à la base je voulais le faire sur le blog que je compte monter, mais le CSS m'écoeure alors je galère...
Le code source suit (amis codeurs, ne regardez pas: c'est sale):
C'est pas génial, je l'ai pas mis sur une forge, et pour la licence, bah... CC-0, BSD-2, WTFPL ou toute autre compatible, au choix. De toute façon ce code est tellement simple que je vois pas comment on pourrait y coller une licence en étant honnête...
Les lecteurs y trouverons des opinions techniques douteuses. Sincèrement: elles sont miennes, je veux bien en discuter, mais je souhaite généralement prouver que C++ peut être aussi léger que le C, avec des avantages nets en terme de code. Ce qui explique mon manque d'appréciation pour, notamment, la STL. Mais bref. Ce code est sale de toute façon, je le sais.
De base, mon idée était de faire un générateur de blog en shell, mais j'ai trouvé aucun outil permettant de merger des lignes en fonction d'une clé, d'où le code précédent.
Bref, j'ai codé cette fonction, elle marche avec mes scripts, et permets bien plus que faire un vulgaire blog: vu qu'elle fait quasi rien, des gens lui trouverons peut-être d'autres usages. Grand bien leur en fasse.
Mon idée était d'utiliser les outils UNIX classiques: sed, awk, cat... afin de passer un fichier source au travers d'une moulinette (par exemple /usr/bin/markdown du paquet discount.deb) et de concaténer ces fichiers avec un header, un footer, etc... mais, ça ne permets pas de générer des index, sommaires ou autres, alors j'ai sorti mon clang++.
Du coup, cet outil est quasiment un pré-compilateur: si ma mémoire est bonne, je récupère une liste de noms de champs de la 1ère ligne, considère que ces champs seront présents à la suite, et les utilise pour générer un texte greppable.
Une fois le texte greppable, je peux revenir au shell. La principale raison pour laquelle je n'ai rien publié est que... bah, je suis une merde en CSS, au point que je meurs d'envie de faire un
<table><line><bla><bla/><line/><table/>dans l'idée... bref, le code est pas assez testé, il est sale, il est largement améliorable, et je le mentionne juste parce que, je me dis que p'tet, quelqu'un le trouveras utile...