moduleDebian.Control.Common(-- * TypesControl'(..),Paragraph'(..),Field'(..),ControlFunctions(..),mergeControls,fieldValue,removeField,prependFields,appendFields,renameField,modifyField,raiseFields,parseControlFromCmd,md5sumField)whereimportText.ParserCombinators.Parsec(ParseError)importSystem.Exit(ExitCode(ExitSuccess,ExitFailure))importSystem.IO(Handle)importSystem.Process(runInteractiveCommand,waitForProcess)importData.List(partition)newtypeControl'a=Control{unControl::[Paragraph'a]}newtypeParagraph'a=Paragraph[Field'a]derivingEq-- |NOTE: we do not strip the leading or trailing whitespace in the-- name or valuedataField'a=Field(a,a)|Commenta-- ^ Lines beginning with #derivingEqclassControlFunctionsawhere-- |'parseControlFromFile' @filepath@ is a simple wrapper function-- that parses @filepath@ using 'pControl'parseControlFromFile::FilePath->IO(EitherParseError(Control'a))-- |'parseControlFromHandle' @sourceName@ @handle@ - @sourceName@ is only used for error reportingparseControlFromHandle::String->Handle->IO(EitherParseError(Control'a))-- |'parseControlFromString' @sourceName@ @text@ - @sourceName@ is only used for error reportingparseControl::String->a->(EitherParseError(Control'a))-- | 'lookupP' @fieldName paragraph@ looks up a 'Field' in a 'Paragraph'.-- @N.B.@ trailing and leading whitespace is /not/ stripped.lookupP::String->(Paragraph'a)->Maybe(Field'a)-- |Strip the trailing and leading space and tab characters from a-- string. Folded whitespace is /not/ unfolded. This should probably-- be moved to someplace more general purpose.stripWS::a->aasString::a->StringmergeControls::[Control'a]->Control'amergeControlscontrols=Control(concatMapunControlcontrols)fieldValue::(ControlFunctionsa)=>String->Paragraph'a->MaybeafieldValuefieldNameparagraph=caselookupPfieldNameparagraphofJust(Field(_,val))->Just$stripWSval_->NothingremoveField::(Eqa)=>a->Paragraph'a->Paragraph'aremoveFieldtoRemove(Paragraphfields)=Paragraph(filterremovefields)whereremove(Field(name,_))=name==toRemoveremove(Comment_)=FalseprependFields::[Field'a]->Paragraph'a->Paragraph'aprependFieldsnewfields(Paragraphfields)=Paragraph(newfields++fields)appendFields::[Field'a]->Paragraph'a->Paragraph'aappendFieldsnewfields(Paragraphfields)=Paragraph(fields++newfields)renameField::(Eqa)=>a->a->Paragraph'a->Paragraph'arenameFieldoldnamenewname(Paragraphfields)=Paragraph(maprenamefields)whererename(Field(name,value))|name==oldname=Field(newname,value)renamefield=fieldmodifyField::(Eqa)=>a->(a->a)->Paragraph'a->Paragraph'amodifyFieldnamef(Paragraphfields)=Paragraph(mapmodifyfields)wheremodify(Field(name',value))|name'==name=Field(name,fvalue)modifyfield=field-- | Move selected fields to the beginning of a paragraph.raiseFields::(Eqa)=>(a->Bool)->Paragraph'a->Paragraph'araiseFieldsf(Paragraphfields)=let(a,b)=partitionf'fieldsinParagraph(a++b)wheref'(Field(name,_))=fnamef'(Comment_)=False-- | Run a command and parse its output as a control file.parseControlFromCmd::ControlFunctionsa=>String->IO(EitherString(Control'a))parseControlFromCmdcmd=do(_,outh,_,handle)<-runInteractiveCommandcmdresult<-parseControlFromHandlecmdoutheither(return.Left.show)(finishhandle)resultwherefinishhandlecontrol=doexitCode<-waitForProcesshandlecaseexitCodeofExitSuccess->return$RightcontrolExitFailuren->return$Left("Failure: "++cmd++" -> "++shown)-- |look up the md5sum file in a paragraph-- Tries several different variations:-- MD5Sum:-- Md5Sum:-- MD5sum:md5sumField::(ControlFunctionsa)=>Paragraph'a->Maybeamd5sumFieldp=casefieldValue"MD5Sum"pofm@(Just_)->mNothing->casefieldValue"Md5Sum"pofm@(Just_)->mNothing->fieldValue"MD5sum"p