无码人妻A片一区二区三区_18禁裸乳无遮挡啪啪无码免费_91精品亚?影视在线?看_人人妻人人爽人人澡AV_国产精品人妻一区二区三区四区_午夜免费影视

中培偉業(yè)IT資訊頻道
您現(xiàn)在的位置:首頁 > IT資訊 > IT運維 > 如何構(gòu)建一個Linux Shell(五)

如何構(gòu)建一個Linux Shell(五)

2020-08-31 18:47:59 | 來源:中培企業(yè)IT培訓網(wǎng)

之前為家介紹了關于如何構(gòu)建一個Linux Shell的一、二、三、四。今天這里為大家介紹的是關于如何構(gòu)建Linux Shell的教程的第五部分。正如我們在前面的部分中所看到的,一個簡單的命令由一個或多個參數(shù)組成。第一個單詞包含我們要執(zhí)行的命令的名稱,其余單詞包含命令的參數(shù)。在外殼程序執(zhí)行命令之前,它將對命令字執(zhí)行字擴展。

單詞擴展是shell提取命令字,檢查它是否包含變量名,路徑名,命令和算術表達式,然后用其值替換每個名稱/命令/表達式的過程。

通常長于原始單詞的結(jié)果單詞可以在稱為字段拆分的過程中分解為一個或多個子單詞。

在這一部分中,我們將實現(xiàn)POSIX定義的7個詞擴展,它們是:波浪號擴展,參數(shù)擴展,算術擴展,命令替換,字段拆分,路徑名擴展和引號刪除。還有其他單詞擴展,例如大括號擴展和流程替換,這些沒有由POSIX定義,因此我們將不在這里討論。完成本課程后,如果您通過實現(xiàn)非POSIX單詞擴展來擴展Shell,將是一個很好的練習。

  單詞擴展過程

當外殼程序執(zhí)行單詞擴展時,它將檢查命令行中的每個單詞以查看其是否包含可能的單詞擴展。擴展可以出現(xiàn)在單詞的任何位置:在單詞的開頭,中間或結(jié)尾。擴展可能還包括整個單詞。

單詞擴展之前有一個$標志。后面的字符$符號指示外殼要執(zhí)行的擴展類型。這些字符由外殼解釋如下:

一個或多個數(shù)字,指示位置參數(shù)的變量擴展。

之一@,*,#,?,-,$,!,要么0,它指示特殊參數(shù)的變量擴展。

一個或多個字母數(shù)字字符和/或下劃線,以字母或下劃線開頭,表示外殼變量名稱。

括號內(nèi)的變量名{和}。

算術展開,被(和)。

命令替換,由((和))。

Shell首先執(zhí)行波浪號擴展,參數(shù)擴展,命令替換和算術擴展,然后進行字段拆分和路徑名擴展。最后,shell從已擴展單詞中刪除了已成為原始單詞一部分的所有引號字符。

  使用文字

當外殼程序執(zhí)行單詞擴展時,該過程可能會導致零個,一個或多個單詞。

structword_s

{

char*data;

intlen;

structword_s*next;

};

  該結(jié)構(gòu)包含以下字段:

data=>表示此單詞的字符串。

len=>的長度data領域。

next=>指向下一個單詞的指針,或者NULL如果這是最后一個單詞。

  當然,我們需要一些函數(shù)來分配和釋放我們的structword_s結(jié)構(gòu)。為此,我們將使用以下功能:

structword_s*make_word(char*str);

voidfree_all_words(structword_s*first);

第一個函數(shù)為該結(jié)構(gòu)分配內(nèi)存,創(chuàng)建單詞字符串的副本,然后返回新分配的單詞。第二個功能釋放單詞結(jié)構(gòu)列表使用的內(nèi)存。您可以在我們的網(wǎng)站中閱讀功能代碼wordexp.c源文件。

  定義一些助手功能

如前所述,詞擴展是一個復雜的過程,為此,我們需要定義許多不同的功能。在深入研究單詞擴展的細節(jié)之前,讓我們先定義一些輔助函數(shù)。

以下列表顯示了我們的輔助函數(shù)的函數(shù)原型,所有這些我們將在wordexp.c源文件:

char*wordlist_to_str(structword_s*word);

voiddelete_char_at(char*str,size_tindex);

intis_name(char*str);

size_tfind_closing_quote(char*data);

size_tfind_closing_brace(char*data);

char*substitute_str(char*s1,char*s2,size_tstart,size_tend);

intsubstitute_word(char**pstart,char**p,size_tlen,char*(func)(char*),intadd_quotes);

以下是這些函數(shù)的功能細目:

wordlist_to_str()=>將擴展單詞的鏈接列表轉(zhuǎn)換為單個字符串。

delete_char_at()=>從字符串中刪除給定索引處的字符。

is_name()=>檢查字符串是否表示有效的變量名。

find_closing_quote()=>當單詞擴展包含開頭的引號",',要么`,我們需要找到匹配的用引號引起來的字符,該字符將引號引起來的字符串括起來。此函數(shù)返回單詞中結(jié)束引號字符的從零開始的索引。

find_closing_brace()=>與上面類似,不同之處在于它找到匹配的右括號。也就是說,如果左括號是{,(,要么[,此函數(shù)返回匹配項的從零開始的索引},),要么]字符。查找引號對對于處理參數(shù)擴展,算術擴展和命令替換很重要。

substitute_str()=>替換的子字符串s1,從位置上的角色開始start到那個位置end,s2串。當單詞擴展是較長單詞的一部分例如,${PATH}/ls,在這種情況下,我們只需要擴展${PATH},然后附加/ls到擴展字符串的末尾。

substitute_word()=>一個幫助程序函數(shù),它調(diào)用其他單詞擴展功能,我們將在以下各節(jié)中對其進行定義。

另外,我們將定義一些函數(shù)來幫助我們處理字符串。我們將在strings.c源文件:

char*strchr_any(char*string,char*chars);

char*quote_val(char*val,intadd_quotes);

intcheck_buffer_bounds(int*count,int*len,char***buf);

voidfree_buffer(intlen,char**buf);

  這些功能的作用如下:

strchr_any()=>類似于strchr(),除了它會在給定的字符串中搜索任何給定的字符。

quote_val()=>執(zhí)行與引號刪除相反的操作,即將字符串轉(zhuǎn)換為帶引號的字符串。

的check_buffer_bounds()和free_buffer()函數(shù)將使我們的后端執(zhí)行器支持可變數(shù)量的命令參數(shù),而不是我們在第二部分中設置的硬限制255。

現(xiàn)在,讓我們編寫函數(shù)來處理每種類型的單詞擴展。

  波浪號擴展

在波浪符號擴展期間,外殼程序?qū)⒉ɡ朔栕址鎿Q為用戶主目錄的路徑名。例如,~和~/波浪線擴展到當前用戶的主目錄,而~john被波浪擴展到用戶John的主目錄,依此類推。除后面的所有字符之外,代字號字符被稱為代字號前綴

要執(zhí)行波浪線擴展,我們將定義tilde_expand()函數(shù),具有以下原型:

char*tilde_expand(char*s);

該函數(shù)接受一個參數(shù):我們要擴展的代字號前綴。如果擴展成功,該函數(shù)返回一個的malloc表示波浪線擴展前綴“d字符串。否則返回NULL。下面是該函數(shù)為擴展代字號前綴所做的工作的快速分解:

如果前綴是~,獲得$HOME外殼變量。如果$HOME被定義而不是NULL,返回其值。否則,通過調(diào)用獲取當前的用戶ID(UID)getuid(),然后將UID傳遞給getpwuid()獲取與當前用戶相對應的密碼數(shù)據(jù)庫條目。的pw_dir密碼數(shù)據(jù)庫條目的“字段”包含函數(shù)返回的主目錄的路徑名。

如果前綴包含其他字符除了前導字符~,我們將這些字母作為要獲取其主目錄的用戶的名稱。我們稱之為getpwnam(),將其傳遞給用戶名,然后返回pw_dir領域。

如果我們無法檢索主目錄,則返回NULL。否則,我們將返回主目錄路徑的malloc副本。

  參數(shù)擴展

在參數(shù)擴展中,外殼程序用變量的值替換外殼程序變量的名稱。參數(shù)擴展使外殼程序可以執(zhí)行諸如echo$PATH。在此示例中,外殼程序?qū)?PATH變量,將其替換為實際的可執(zhí)行路徑。

為了向shell發(fā)出我們要擴展shell變量的信號,我們在變量名稱前添加一個$標志。也就是說,擴大PATH,USER和SHELL變量,我們需要傳遞$PATH,$USER和$SHELL將單詞分別傳遞給shell或者,我們可以將這些變量擴展傳遞給shellshell,如下所示:${PATH},${USER}和${SHELL}。Shell變量名稱可以包含字母,數(shù)字和下劃線的任意組合。名稱可以包含大寫字母或小寫字母,盡管按照慣例,大寫名稱保留用于標準Shell變量。

我們可以使用參數(shù)擴展修飾符來控制外殼如何執(zhí)行參數(shù)擴展,該修飾符告訴外殼我們要擴展值的哪一部分,以及在沒有給定名稱的外殼變量的情況下該怎么做。下表總結(jié)了參數(shù)擴展修飾符由POSIX定義的修飾符在“描述”列中由POSIX單詞標記。大多數(shù)外殼程序都支持其他修飾符,我們將不在此處討論。有關非POSIX修飾符的更多信息,請參見您的Shell的手冊頁。

要執(zhí)行參數(shù)擴展,我們將定義var_expand()函數(shù),具有以下原型:

char*var_expand(char*orig_var_name);

該函數(shù)接受一個參數(shù):我們要擴展的參數(shù)。如果擴展成功,該函數(shù)將返回一個包含擴展值的malloc'd字符串。否則返回NULL。下面是該函數(shù)為擴展變量名以獲取其值而執(zhí)行的操作的快速細分:

如果變量名用大括號括起來,請刪除大括號,因為它們不是變量名本身的一部分。如果名稱以#,我們需要獲取變量名稱的長度。

如果變量名稱包含冒號,我們將使用它來將名稱與單詞或模式分開。單詞或圖案的使用如上表所示。獲取具有給定變量名稱的符號表條目。獲取符號表條目的值。

如果該值為空或為空,請使用擴展中提供的替代詞。

如果該值不為空,則將該值用作擴展結(jié)果。要使外殼執(zhí)行模式匹配${parameter#word}和${parameter%word}擴展,我們需要兩個幫助函數(shù):match_suffix()和match_prefix()。我們不會在這里討論這些功能,但是您可以從此鏈接中閱讀它們的代碼。

如果擴展修飾符為${parameter:=word},我們需要將符號表條目的值設置為剛剛擴展的值。

如果擴展以#,獲取擴展值的長度并將其用作最終結(jié)果。返回擴展值的malloc'd副本或其長度,視情況而定。

  命令替換

在命令替換中,shell派生一個進程來運行命令,然后用命令的輸出替換命令替換擴展。例如,在以下循環(huán)中:

foriin$(ls);doecho$i;done

外殼分叉一個過程,其中l(wèi)s命令運行。該命令的輸出是當前目錄中的文件列表。Shell獲取該輸出,將其拆分為單詞列表,然后一次將這些單詞提供給循環(huán)。在循環(huán)的每次迭代中,變量$i被分配了列表中下一個文件的名稱。

此名稱將傳遞給echo命令,該命令在單獨的行上輸出名稱。

命令替換可以寫成$(command),要么`command`。要執(zhí)行命令替換,我們將定義command_substitute()函數(shù),具有以下原型:

char*command_substitute(char*orig_cmd);

該函數(shù)接受一個參數(shù):我們要執(zhí)行的命令。如果擴展成功,則該函數(shù)將返回一個malloc'd字符串,表示命令的輸出。如果擴展失敗,或者命令沒有輸出任何內(nèi)容,則函數(shù)返回NULL。

下面是該函數(shù)為擴展命令替換而執(zhí)行的操作的快速細分:

根據(jù)使用的格式,我們首先刪除$()或反引號``。這給我們留下了我們需要執(zhí)行的命令。

呼叫popen()創(chuàng)建一個管道。我們將要執(zhí)行的命令傳遞給popen(),我們得到一個指向FILE流,我們將從中讀取命令的輸出。

呼叫fread()從管道讀取命令的輸出。將讀取的字符串存儲在緩沖區(qū)中。

刪除所有尾隨換行符。

關閉管道,并使用命令輸出返回緩沖區(qū)。

  算術擴展

使用算術擴展,我們可以讓外殼執(zhí)行不同的算術運算,并將結(jié)果用于執(zhí)行其他命令。盡管POSIX要求外殼程序僅支持帶符號的長整數(shù)算法,但許多外殼程序都支持浮點算法。

此外,盡管大多數(shù)外殼程序都不需要外殼程序來支持任何數(shù)學函數(shù)。對于簡單的shell,我們將僅支持帶符號的長整數(shù)算法,而沒有數(shù)學函數(shù)支持。

算術擴展寫為$((expression))。

要執(zhí)行擴展,我們將定義arithm_expand()函數(shù),具有以下原型:

char*arithm_expand(char*expr);

的arithm_expand()函數(shù)接收包含算術表達式的字符串,執(zhí)行必要的計算,然后以malloc'd字符串的形式返回結(jié)果。該函數(shù)及其相關的幫助器函數(shù)既復雜又冗長,但這是主要亮點:

算術表達式轉(zhuǎn)換為反向波蘭表示法,更易于分析和計算。RPN由一系列算術運算組成,其中運算符遵循其操作數(shù)。例如,RPNx-y是xy-,以及3+4×(2?1)是3421?×+。

在轉(zhuǎn)換過程中,算術運算符被壓入一個運算符堆棧,我們將從中彈出每個運算符并稍后執(zhí)行其運算。同樣,操作數(shù)被添加到它們自己的堆棧中。

一次將操作員從堆棧中彈出,然后對操作員進行檢查。根據(jù)運算符的類型,一個或兩個操作數(shù)從堆棧中彈出。控制此過程的規(guī)則是調(diào)車場算法的規(guī)則,您可以在此處閱讀。

結(jié)果將轉(zhuǎn)換為字符串,然后將其返回給調(diào)用方。

  場分裂

在字段拆分期間,shell會獲取參數(shù)擴展,命令替換和算術擴展的結(jié)果,并將它們拆分為一個或多個部分,我們將其稱為字段。該過程取決于$IFS外殼變量。IFS是一個歷史術語,代表內(nèi)部字段分隔符,它起源于Unixshell沒有內(nèi)置數(shù)組類型的時間。

作為解決方法,早期的Unixshell必須找到另一種表示多成員數(shù)組的方式。外殼程序?qū)⒁詥蝹€字符串將數(shù)組成員連接在一起,并以空格分隔。當外殼程序需要檢索數(shù)組成員時,它將字符串分成一個或多個字段。的$IFS變量告訴外殼程序在何處確切地中斷該字符串。

  外殼解釋$IFS如下字符:

如果值$IFS是空格,制表符和換行符,或者如果未設置變量,則在輸入的開頭或結(jié)尾處的空格,制表符或換行符的任何序列都將被忽略,并且輸入中這些字符的任何序列應定界領域。

如果值$IFS為null,不得執(zhí)行任何字段拆分。

否則,應依次適用以下規(guī)則:(a)$IFS在輸入的開頭和結(jié)尾應忽略空格。(b)輸入中的每次出現(xiàn)$IFS不是的字符$IFS空白,以及任何相鄰的空白$IFS如前所述,空白應界定一個字段。(c)非零長度$IFS空白應界定一個字段。

要執(zhí)行擴展,我們將定義field_split()函數(shù),具有以下原型:

structword_s*field_split(char*str);

路徑名擴展

在擴展路徑名期間,shell將以給定的模式匹配一個或多個文件名。除特殊字符外,該模式還可以包含普通字符*,?和[],也稱為“通配符”。

星號*匹配任意長度的字符,匹配一個字符,并且方括號引入正則表達式(RE)括號表達式。擴展的結(jié)果是名稱與模式匹配的文件列表。

要執(zhí)行擴展,我們將定義pathnames_expand()函數(shù),具有以下原型:

structword_s*pathnames_expand(structword_s*words);

此函數(shù)接受一個參數(shù):指向我們要路徑名擴展的單詞的鏈接列表中第一個單詞的指針。對于每個單詞,該函數(shù)執(zhí)行以下操作:

檢查單詞是否包含任何通配符*,?和[],通過調(diào)用輔助函數(shù)has_glob_chars(),我們將在源文件中定義pattern.c。如果單詞包含通配符,我們將其視為需要匹配的模式;否則,我們移至下一個單詞。

獲取名稱與模式匹配的文件列表,不包括特殊名稱.和..。我們將模式匹配委托給另一個幫助函數(shù),get_filename_matches(),我們將在同一源文件中定義pattern.c。

將匹配的文件名添加到最終列表。

移至下一個單詞并循環(huán)。

返回與所有給定單詞匹配的文件名列表。

  刪除報價

單詞擴展過程的最后一步是刪除引號。引用用于刪除某些字符到shell的特殊含義。外殼會以特殊方式處理某些字符,例如反斜杠和引號。要禁止這種行為,我們需要引用那些字符以強制外殼將它們視為普通字符。

我們可以使用以下三種方式之一對字符進行引用:使用反斜杠,單引號或雙引號。反斜杠字符用于保留反斜杠后面的字符的字面意思。這類似于我們用C語言轉(zhuǎn)義字符的方式。

單引號保留引號內(nèi)所有字符的字面含義,即外殼程序不嘗試對單引號字符串進行單詞擴展。

雙引號與單引號類似,不同之處在于外殼可以識別反引號,反斜杠和$標志。也就是說,外殼程序可以在雙引號字符串內(nèi)執(zhí)行單詞擴展。

要執(zhí)行報價刪除,我們將定義remove_quotes()函數(shù),具有以下原型:

voidremove_quotes(structword_s*wordlist)。

  放在一起

現(xiàn)在我們有了詞擴展功能,是時候?qū)⑵浣Y(jié)合在一起了。在本節(jié)中,我們將編寫主要的單詞擴展功能,我們將調(diào)用該功能來執(zhí)行單詞擴展。反過來,此函數(shù)將調(diào)用其他函數(shù)來執(zhí)行單詞擴展的各個步驟。

我們的主要功能是word_expand(),我們將在源文件中定義wordexp.c:

structword_s*word_expand(char*orig_word);

這是為了對作為唯一參數(shù)傳遞的單詞執(zhí)行單詞擴展的功能:

創(chuàng)建原始單詞的副本。我們將在此副本上執(zhí)行單詞擴展,以便在出現(xiàn)任何問題時將原始單詞保留完整。逐字掃描單詞,尋找特殊字符~,",',`,=,和$。如果找到上述字符之一,請致電substitute_word(),這將調(diào)用相應的單詞擴展功能。

跳過任何沒有特殊含義的字符。完成單詞擴展后,通過調(diào)用執(zhí)行字段拆分field_split()。通過調(diào)用執(zhí)行路徑名擴展pathnames_expand()。通過調(diào)用執(zhí)行報價刪除remove_quotes()。返回擴展單詞的列表。

更新掃描儀

在本教程的第二部分中,我們編寫了tokenize()函數(shù),我們用來獲取輸入令牌。到目前為止,我們的tokenize()函數(shù)不知道如何處理帶引號的字符串和轉(zhuǎn)義字符。要添加此功能,我們需要更新代碼。打開scanner.c文件,并將以下代碼添加到tokenize()功能之后switch聲明的開頭括號:

case'"':

case''':

case'`':

add_to_buf(nc);

i=find_closing_quote(src->buffer+src->curpos);

if(!i)

{

src->curpos=src->bufsize;

fprintf(stderr,"error:missingclosingquote'%c' ",nc);

return&eof_token;

}

while(i--)

{

add_to_buf(next_char(src));

}

break;

case'\':

nc2=next_char(src);

if(nc2==' ')

{

break;

}

add_to_buf(nc);

if(nc2>0)

{

add_to_buf(nc2);

}

break;

case'$':

add_to_buf(nc);

nc=peek_char(src);

if(nc=='{'||nc=='(')

{

i=find_closing_brace(src->buffer+src->curpos+1);

if(!i)

{

src->curpos=src->bufsize;

fprintf(stderr,"error:missingclosingbrace'%c' ",nc);

return&eof_token;

}

while(i--)

{

add_to_buf(next_char(src));

}

}

elseif(isalnum(nc)||nc=='*'||nc=='@'||nc=='#'||

nc=='!'||nc=='?'||nc=='$')

{

add_to_buf(next_char(src));

}

break;

現(xiàn)在,我們的詞法掃描器知道如何識別和跳過帶引號的字符串,轉(zhuǎn)義字符和其他單詞擴展構(gòu)造。在此鏈接中查看更新的詞法掃描程序代碼。

  更新執(zhí)行器最后,我們需要更新后端執(zhí)行程序,以便可以:

在執(zhí)行命令之前,對命令的參數(shù)執(zhí)行單詞擴展。每個命令支持超過255個參數(shù)。打開executor.c文件,導航到do_simple_command()函數(shù)并找到以下幾行:

intargc=0;

longmax_args=255;

char*argv[max_args+1];

char*str;

while(child)

{

...

}

argv[argc]=NULL;

并用以下代碼替換它們:

intargc=0;

inttargc=0;

char**argv=NULL;

char*str;

while(child)

{

str=child->val.str;

structword_s*w=word_expand(str);

if(!w)

{

child=child->next_sibling;

continue;

}

structword_s*w2=w;

while(w2)

{

if(check_buffer_bounds(&argc,&targc,&argv))

{

str=malloc(strlen(w2->data)+1);

if(str)

{

strcpy(str,w2->data);

argv[argc++]=str;

}

}

w2=w2->next;

}

free_all_words(w);

child=child->next_sibling;

}

if(check_buffer_bounds(&argc,&targc,&argv))

{

argv[argc]=NULL;

}

使用此代碼,執(zhí)行程序調(diào)用word_expand()在每個命令自變量上,并將擴展的單詞添加到自變量列表,我們最終將其傳遞給命令。該列表可以根據(jù)需要增長,這要歸功于我們check_buffer_bounds()函數(shù),根據(jù)需要將內(nèi)存分配給緩沖區(qū)。

現(xiàn)在剩下的就是在執(zhí)行完命令后釋放參數(shù)列表,然后返回調(diào)用者。為此我們致電:

free_buffer(argc,argv);

在三個不同的位置:執(zhí)行內(nèi)置實用程序后,如果fork()返回錯誤狀態(tài),然后waitpid()已經(jīng)回來了。在此鏈接中查看更新的執(zhí)行程序代碼。

  編譯外殼

讓我們編譯一下shell。打開您喜歡的終端模擬器,導航到源目錄,并確保其中有19個文件和2個子目錄:

現(xiàn)在,使用以下命令編譯外殼程序:

  make

如果一切順利gcc不應輸出任何內(nèi)容,并且應該有一個名為shell在當前目錄中:

現(xiàn)在通過運行來調(diào)用shell./shell,然后嘗試使用我們的單詞擴展功能并檢查結(jié)果:$echo*Makefilebuildbuiltinsexecutor.cexecutor.hinitsh.cmain.cnode.cnode.hparser.cparser.hpattern.cprompt.cscanner.cscanner.hshellshell.hshunt.csource.csource.hstrings.csymtabwordexp.c

$echo'*'

*

$echo~

/home/user

$echo~/Downloads

/home/user/Downloads

$echo${A=value}

value

$echo$A

value

$echo$((2+7))

9

就是今天。我們的外殼現(xiàn)在可以處理各種單詞擴展。玩弄外殼,看看從不同類型的單詞擴展中可以得到什么結(jié)果。將結(jié)果與從默認Shell獲得的結(jié)果進行比較。

在此,我們已經(jīng)取得了長足的進步,提供了許多代碼,其中大多數(shù)代碼沒有時間或空間來詳細研究。您可能需要花一些時間閱讀我們存儲庫中的代碼,以使自己熟悉單詞擴展過程。想了解更多關于Linux Shell 的信息,請繼續(xù)關注中培偉業(yè)。

標簽: IT運維
主站蜘蛛池模板: 国产精品人妻一区二区网站 | 一级片视频在线播放 | 在线精品自偷自拍无码中文 | 777国产偷窥盗摄精品视频 | 国产综合13P | 国产成人精品在线观看 | 99久久久免费精品国产一区二区 | 天堂资源中文在线 | 超碰人人模人人爽人人喊手机版 | 亚洲激情偷拍 | 天天躁日日躁狠狠躁欧美老妇 | 天天摸天天碰天天添 | 免费爱爱视频 | 亚洲第一色| 高潮的毛片激情久久精品 | XXX欧美性兽交 | 绯色av蜜臀一区二区中文字幕 | 日韩欧美天堂 | 91视频免费?看 | 亚洲美女在线视频 | 欧美牲交VIDEOSSEXES | 精品一区二区三区中文字幕视频 | 国产精品热 | 成人18视频免费69 | 不卡一区视频 | 国产女性无套免费看网站 | 农民工嫖妓50岁老熟女 | 亚洲精品一区二区三区在线观看 | 中文亚色 | 97在线免费视频观看 | 自拍偷拍中文字幕 | 美女很黄免费 | 久久日本三级韩国三级 | 欧美成人看片一区二区三区尤物 | 亚洲伦无码中文字幕另类 | 亚洲成人综合社区 | 中文幕专区 | 91久久久久| 免费特级黄毛片在线成人观看 | 精品国产一区二 | 亚洲最大日韩中文字幕另类 |